diff --git a/cmd/bulk.go b/cmd/bulk.go index 0dde21e9af55982fec00edfc171984d1387594a4..4b24061d98dcd67d33968244941bfe83200b4e9b 100644 --- a/cmd/bulk.go +++ b/cmd/bulk.go @@ -7,9 +7,13 @@ import ( "strings" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" - "gitlab.c3sl.ufpr.br/tss24/useradm/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/ltype" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var ( @@ -62,7 +66,7 @@ func init() { } func bulkCreate(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts group := args[0] path := args[1] @@ -86,13 +90,13 @@ func bulkCreate(cmd *cobra.Command, args []string) error { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - users, err := model.GetAllUsersLDAP(l) + users, err := manage.GetAllUsers(l) if err != nil { return err } @@ -110,22 +114,22 @@ func bulkCreate(cmd *cobra.Command, args []string) error { continue } - if u.Ltype == model.Initials && u.GRR == "_" { + if u.Ltype == ltype.Initials && u.GRR == "_" { fmt.Printf("Entry %v: ini login type needs GRR\n Skipping...", i+1) continue } - err = u.GenMissingFields() + err = manage.GenMissingFields(&u) if err != nil { return err } - if !model.NameExists(users, u.Name) { + if !manage.NameExists(users, u.Name) { if !opts.Confirm { fmt.Printf("Ready to create: %v\n", u.FullToString()) } utils.ConfirmationPrompt(opts.Confirm, "user creation") - err = u.Create(l) + err = manage.CreateUser(l, &u) if err != nil { return err } @@ -139,8 +143,8 @@ func bulkCreate(cmd *cobra.Command, args []string) error { return nil } -func extractModelsFile(opts model.Opts, group, path string) ([]model.User, error) { - var users []model.User +func extractModelsFile(opts opts.Opts, group, path string) ([]user.User, error) { + var users []user.User file, err := os.Open(path) if err != nil { @@ -148,16 +152,16 @@ func extractModelsFile(opts model.Opts, group, path string) ([]model.User, error } defer file.Close() - var ltype model.LoginType + var lt ltype.LoginType if group == "ppginf" || group == "espinf" { - ltype = model.LastName + lt = ltype.LastName } else { - ltype = model.Initials + lt = ltype.Initials } scanner := bufio.NewScanner(file) for scanner.Scan() { - var u model.User + var u user.User line := scanner.Text() parts := strings.Split(line, ":") u.Name = parts[0] @@ -166,8 +170,8 @@ func extractModelsFile(opts model.Opts, group, path string) ([]model.User, error u.GRR = parts[1] } u.UID = "" + u.Ltype = lt u.GID = group - u.Ltype = ltype u.Resp = opts.Resp u.Expiry = opts.Expiry users = append(users, u) diff --git a/cmd/group/create.go b/cmd/group/create.go index 25e2fa538c68c77249db17a2e3e58f4e7d0241aa..f8bc36ec5128f8867b8ada73d9776fe2d23568bd 100644 --- a/cmd/group/create.go +++ b/cmd/group/create.go @@ -4,8 +4,9 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var CreateCmd = &cobra.Command{ @@ -20,20 +21,20 @@ func init() { } func createGroupCmd(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts err := opts.RetrieveOpts(cmd) if err != nil { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - groups, err := model.GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -44,7 +45,7 @@ func createGroupCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("Groupname already exists") } - GIDNumber, err := model.GetNewGIDNumber(l) + GIDNumber, err := auth.GetNewGIDNumLDAP(l) if err != nil { return err } @@ -53,7 +54,7 @@ func createGroupCmd(cmd *cobra.Command, args []string) error { fmt.Printf("gidNumber: %v\n", GIDNumber) utils.ConfirmationPrompt(opts.Confirm, "group creation") - err = model.LDAPCreateGroup(l, args[0], GIDNumber) + err = auth.CreateGroupLDAP(l, args[0], GIDNumber) if err != nil { return err } diff --git a/cmd/group/delete.go b/cmd/group/delete.go index f021b47f0d5babf63aac59bea73b7a34b7ef72ed..3d395307922fdf3b4d7af6361cf88c5f9578199b 100644 --- a/cmd/group/delete.go +++ b/cmd/group/delete.go @@ -4,12 +4,13 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var DeleteCmd = &cobra.Command{ - Use: "delete", + Use: "delete [groupname]", Short: "Delete a group", Args: cobra.ExactArgs(1), RunE: deleteCmd, @@ -20,20 +21,20 @@ func init() { } func deleteCmd(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts err := opts.RetrieveOpts(cmd) if err != nil { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - groups, err := model.GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -47,7 +48,7 @@ func deleteCmd(cmd *cobra.Command, args []string) error { fmt.Printf("gidNumber: %v\n", gidNumber) utils.ConfirmationPrompt(opts.Confirm, "group deletion") - err = model.LDAPDeleteGroup(l, args[0]) + err = auth.DeleteGroupLDAP(l, args[0]) if err != nil { return err } diff --git a/cmd/user/block.go b/cmd/user/block.go index c251bbb92f1eb29985fcefce2989a7fa488e45e9..3436358ba79529f478bc38635f4859da38e10a87 100644 --- a/cmd/user/block.go +++ b/cmd/user/block.go @@ -4,8 +4,9 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var BlockCmd = &cobra.Command{ @@ -26,14 +27,14 @@ func blockUserCmd(cmd *cobra.Command, args []string) error { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() // Locate returns a error if more than one user was found - u, err := model.Locate(l, args[0]) + u, err := manage.Locate(l, args[0]) if err != nil { return err } @@ -41,7 +42,7 @@ func blockUserCmd(cmd *cobra.Command, args []string) error { fmt.Printf("%v\n", u.FullToString()) utils.ConfirmationPrompt(confirm, "user blocking") - err = u.Block() + err = manage.Block(u) if err != nil { return err } diff --git a/cmd/user/create.go b/cmd/user/create.go index 73ac9f3b1f807bd882b7f2b2d1d9df7d5e358b45..7ac5a6a8b0b2b9602fc4d67c7d013373f53f9465 100644 --- a/cmd/user/create.go +++ b/cmd/user/create.go @@ -4,9 +4,12 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" - "gitlab.c3sl.ufpr.br/tss24/useradm/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var CreateCmd = &cobra.Command{ @@ -40,7 +43,7 @@ func init() { } func createUserCmd(cmd *cobra.Command, args []string) error { - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } @@ -52,7 +55,7 @@ func createUserCmd(cmd *cobra.Command, args []string) error { return err } - users, err := model.GetAllUsersLDAP(l) + users, err := manage.GetAllUsers(l) if err != nil { return err } @@ -62,14 +65,14 @@ func createUserCmd(cmd *cobra.Command, args []string) error { // print a warning if name was found, // better because UID is forced to be unique - if model.NameExists(users, usr.Name) { + if manage.NameExists(users, usr.Name) { fmt.Printf("WARNING: Name was found in LDAP database!\n") confirm = false } utils.ConfirmationPrompt(confirm, "user creation") - err = usr.Create(l) + err = manage.CreateUser(l, &usr) if err != nil { return err } @@ -83,9 +86,9 @@ func createUserCmd(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 +func createNewUserModel(cmd *cobra.Command) (user.User, bool, error) { + var u user.User + var opts opts.Opts err := opts.RetrieveOpts(cmd) if err != nil { @@ -104,7 +107,7 @@ func createNewUserModel(cmd *cobra.Command) (model.User, bool, error) { return u, false, fmt.Errorf("GRR is required for \"ini\" login type") } - u = model.User{ + u = user.User{ UID: opts.UID, GID: opts.GID, GRR: opts.GRR, @@ -121,7 +124,7 @@ func createNewUserModel(cmd *cobra.Command) (model.User, bool, error) { u.Ltype.Parse(opts.Ltype) // this will infer the fields the user didn't provide (blank) - err = u.GenMissingFields() + err = manage.GenMissingFields(&u) if err != nil { return u, false, err } @@ -129,20 +132,20 @@ func createNewUserModel(cmd *cobra.Command) (model.User, bool, error) { return u, opts.Confirm, nil } -func validateInputs(opts model.Opts) error { +func validateInputs(opts opts.Opts) error { - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - users, err := model.GetAllUsersLDAP(l) + users, err := manage.GetAllUsers(l) if err != nil { return err } - groups, err := model.GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } diff --git a/cmd/user/group.go b/cmd/user/group.go index 6242aa827c0d9fcabe3fdfaea050217fb3508fd4..bff48deed61210b05981f3f710a08cfa72208fec 100644 --- a/cmd/user/group.go +++ b/cmd/user/group.go @@ -4,8 +4,9 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var GroupCmd = &cobra.Command{ @@ -26,19 +27,19 @@ func groupUserCmd(cmd *cobra.Command, args []string) error { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() // Locate returns a error if more than one user was found - u, err := model.Locate(l, args[0]) + u, err := manage.Locate(l, args[0]) if err != nil { return err } - groups, err := model.GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -51,7 +52,7 @@ func groupUserCmd(cmd *cobra.Command, args []string) error { fmt.Printf("%v\n", u.FullToString()) utils.ConfirmationPrompt(confirm, "user group change") - err = u.ChangeBaseGroupLDAP(l, args[1]) + err = auth.ChangeBaseGroupLDAP(l, u.DN, args[1]) if err != nil { return err } diff --git a/cmd/user/mod.go b/cmd/user/mod.go index 5a81ddf8a09c6e46a256ca4a547506d5595cf97d..761579dc700649e1e1a31893407eb2430ee24b62 100644 --- a/cmd/user/mod.go +++ b/cmd/user/mod.go @@ -8,9 +8,12 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" - "gitlab.c3sl.ufpr.br/tss24/useradm/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" "gopkg.in/yaml.v3" ) @@ -38,20 +41,20 @@ func init() { } func modUserCmd(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - users, err := model.GetAllUsersLDAP(l) + users, err := manage.GetAllUsers(l) if err != nil { return err } - groups, err := model.GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -61,7 +64,7 @@ func modUserCmd(cmd *cobra.Command, args []string) error { return err } - curr, err := model.Locate(l, args[0]) + curr, err := manage.Locate(l, args[0]) if err != nil { return err } @@ -115,11 +118,11 @@ func modUserCmd(cmd *cobra.Command, args []string) error { } if oldGroup != changes.Group { - if err := model.DelFromGroupLDAP(l, curr.UID, curr.GID); err != nil { + if err := auth.DelFromGroupLDAP(l, curr.UID, curr.GID); err != nil { return err } - if err := model.AddToGroupLDAP(l, curr.UID, changes.Group); err != nil { + if err := auth.AddToGroupLDAP(l, curr.UID, changes.Group); err != nil { return err } } @@ -136,7 +139,7 @@ Output: %v`, err) } func genRequest(groups map[string]string, - curr model.User, changes cfg) (*ldap.ModifyRequest, error) { + curr *user.User, changes cfg) (*ldap.ModifyRequest, error) { req := ldap.NewModifyRequest(curr.DN, nil) @@ -174,19 +177,21 @@ func genRequest(groups map[string]string, req.Replace("gidNumber", []string{gidNumber}) } - changed := applyChangesToUser(curr, changes) + gecos := curr.Gecos - changed.GenGecos() + applyChangesToUser(curr, changes) + + curr.GenGecos() // changed gecos - if curr.Gecos != changed.Gecos { - req.Replace("gecos", []string{changed.Gecos}) + if curr.Gecos != gecos { + req.Replace("gecos", []string{curr.Gecos}) } return req, nil } -func applyChangesToUser(c model.User, n cfg) model.User { +func applyChangesToUser(c *user.User, n cfg) { c.GRR = n.GRR c.GID = n.Group c.Name = n.Name @@ -194,7 +199,6 @@ func applyChangesToUser(c model.User, n cfg) model.User { c.Shell = n.Shell c.Status = n.Status c.Expiry = n.Expiry - return c } // generates a yaml file of a config state for the user to edit diff --git a/cmd/user/remove.go b/cmd/user/remove.go index 58a40e484259b955a9bd20cbdf08efd1a993502e..3be7c7e1b681600cdd76f46bc0cd877bdc190c88 100644 --- a/cmd/user/remove.go +++ b/cmd/user/remove.go @@ -6,8 +6,10 @@ import ( "path/filepath" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var RemoveCmd = &cobra.Command{ @@ -22,7 +24,7 @@ func init() { } func removeUserCmd(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts success := false err := opts.RetrieveOpts(cmd) @@ -30,13 +32,13 @@ func removeUserCmd(cmd *cobra.Command, args []string) error { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - u, err := model.Locate(l, args[0]) + u, err := manage.Locate(l, args[0]) if err != nil { return err } @@ -44,11 +46,11 @@ func removeUserCmd(cmd *cobra.Command, args []string) error { defer func() { if !success { log.Println("Found error, rolling back dirs...") - _ = utils.MoveAndChown(filepath.Join(model.NO_BKP_TRASH, + _ = utils.MoveAndChown(filepath.Join(manage.NO_BKP_TRASH, filepath.Base(u.Nobackup)), u.Nobackup, u.UID, u.GID) - _ = utils.MoveAndChown(filepath.Join(model.HOME_TRASH, + _ = utils.MoveAndChown(filepath.Join(manage.HOME_TRASH, filepath.Base(u.Homedir)), u.Homedir, u.UID, u.GID) - _ = utils.MoveAndChown(filepath.Join(model.WEB_TRASH, + _ = utils.MoveAndChown(filepath.Join(manage.WEB_TRASH, filepath.Base(u.Webdir)), u.Webdir, u.UID, u.GID) } }() @@ -57,7 +59,7 @@ func removeUserCmd(cmd *cobra.Command, args []string) error { utils.ConfirmationPrompt(opts.Confirm, "user removal") - err = u.DirsToTrash() + err = manage.MoveDirsToTrash(u) if err != nil { // scuffed i know, in the edge case there is already a // directory in trash with the username this will fail @@ -71,7 +73,7 @@ func removeUserCmd(cmd *cobra.Command, args []string) error { return err } - err = model.DelFromLDAP(u.UID) + err = auth.DelFromLDAP(l, u.UID) if err != nil { return err } diff --git a/cmd/user/reset.go b/cmd/user/reset.go index 22cf5da32391ff42961ff3b96e8dd5d3590228d4..3a7d6770655cbf8b2c1e4645406516ebd00ec0a8 100644 --- a/cmd/user/reset.go +++ b/cmd/user/reset.go @@ -4,8 +4,9 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var ResetCmd = &cobra.Command{ @@ -25,7 +26,7 @@ func resetPassCmd(cmd *cobra.Command, args []string) error { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } @@ -36,13 +37,13 @@ func resetPassCmd(cmd *cobra.Command, args []string) error { } login := args[0] - _, err = model.Locate(l, login) + _, err = manage.Locate(l, login) if err != nil { return err } utils.ConfirmationPrompt(false, "password reset") - err = model.ModKRBPassword(login, pass) + err = auth.ModKRBPassword(login, pass) if err != nil { return err } diff --git a/cmd/user/show.go b/cmd/user/show.go index 35dd399457618f044fb4452306f7696424cb0973..3338aa4cd77b1ac98ea8fecc3304a3707ebd2df9 100644 --- a/cmd/user/show.go +++ b/cmd/user/show.go @@ -4,7 +4,9 @@ import ( "fmt" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" ) var ShowCmd = &cobra.Command{ @@ -30,15 +32,15 @@ func init() { } func searchUserCmd(cmd *cobra.Command, args []string) error { - var o model.Opts + var o opts.Opts - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - users, err := model.GetAllUsersLDAP(l) + users, err := manage.GetAllUsers(l) if err != nil { return err } @@ -48,7 +50,7 @@ func searchUserCmd(cmd *cobra.Command, args []string) error { return err } - found := model.Search(users, o.Ignore, o.Exact, o.UID, + found := manage.Search(users, o.Ignore, o.Exact, o.UID, o.GID, o.Name, o.GRR, o.Status, o.Homedir) if len(found) == 0 { diff --git a/cmd/user/temp.go b/cmd/user/temp.go index 0af802a5bcdc09d1e9bbcc0a55d443b1fa2c886e..101a2fd0475692f5538ebe92ec925eaa26c51f63 100644 --- a/cmd/user/temp.go +++ b/cmd/user/temp.go @@ -6,9 +6,13 @@ import ( "strconv" "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" - "gitlab.c3sl.ufpr.br/tss24/useradm/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/ltype" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/validate" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var TempCmd = &cobra.Command{ @@ -32,20 +36,20 @@ func init() { } func tempCreate(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - users, err := model.GetAllUsersLDAP(l) + users, err := manage.GetAllUsers(l) if err != nil { return err } - groups, err := model.GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -57,7 +61,7 @@ func tempCreate(cmd *cobra.Command, args []string) error { for i := 1; i <= opts.Number; i++ { login := opts.UID + strconv.Itoa(i) - if model.LoginExists(users, login) { + if manage.LoginExists(users, login) { return fmt.Errorf("User found with login %v, won't overwrite", login) } } @@ -67,13 +71,13 @@ func tempCreate(cmd *cobra.Command, args []string) error { return err } - base := model.User{ + base := user.User{ GRR: "_", UID: opts.UID, GID: opts.GID, Name: "_", Resp: opts.Resp, - Ltype: model.LoginTypeUnknown, + Ltype: ltype.LoginTypeUnknown, Shell: "/bin/bash", Status: "Active", Expiry: opts.Expiry, @@ -100,8 +104,8 @@ func tempCreate(cmd *cobra.Command, args []string) error { fmt.Println("Error found, cleaning up...") for ; i > 0; i-- { istring := strconv.Itoa(i) - _ = model.DelKRBPrincipal(base.UID + istring) - _ = model.DelFromLDAP(base.UID + istring) + _ = auth.DelKRBPrincipal(base.UID + istring) + _ = auth.DelFromLDAP(l, base.UID+istring) _ = os.RemoveAll(base.Nobackup + istring) _ = os.RemoveAll(base.Homedir + istring) } @@ -121,18 +125,18 @@ func tempCreate(cmd *cobra.Command, args []string) error { return nil } -func createTempUser(base model.User, num int) error { +func createTempUser(base user.User, num int) error { var err error var groups map[string]string numstring := strconv.Itoa(num) - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() - groups, err = model.GetAllGroupsLDAP(l) + groups, err = auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -141,7 +145,7 @@ func createTempUser(base model.User, num int) error { base.UID = base.UID + numstring // gen dn - base.SetDN(base.UID) + base.GenDN() // no webdir for temps @@ -164,15 +168,16 @@ func createTempUser(base model.User, num int) error { } // gen newuid - err = base.GetNewUIDNumber(l) + uidNum, err := auth.GetNewUIDNumLDAP(l) if err != nil { return err } + base.UIDNumber = uidNum fmt.Printf("Pronto para criar:\n%v\n\n", base.FullToString()) utils.ConfirmationPrompt(false, "user creation") - err = base.Create(l) + err = manage.CreateUser(l, &base) if err != nil { return err } diff --git a/cmd/user/unblock.go b/cmd/user/unblock.go index 701efd1bf551ff882f65bee7d57ada6a3cbee6e8..9d1469466efcb3c4046f70ee9fc8fe95c2afa964 100644 --- a/cmd/user/unblock.go +++ b/cmd/user/unblock.go @@ -2,8 +2,10 @@ package user import ( "github.com/spf13/cobra" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/opts" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) var UnblockCmd = &cobra.Command{ @@ -20,26 +22,26 @@ func init() { } func unblockUserCmd(cmd *cobra.Command, args []string) error { - var opts model.Opts + var opts opts.Opts err := opts.RetrieveOpts(cmd) if err != nil { return err } - l, err := model.ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() login := args[0] - u, err := model.Locate(l, login) + u, err := manage.Locate(l, login) if err != nil { return err } utils.ConfirmationPrompt(opts.Confirm, "user unblocking") - u.Unblock(opts.Passwd) + manage.Unblock(u, opts.Passwd) return nil } diff --git a/model/krb.go b/internal/auth/krb.go similarity index 99% rename from model/krb.go rename to internal/auth/krb.go index d461d8a900848d78cb2725f447742a7fa54f3d90..4c4c33c6043b58734b0f9746d65274f66a4102d1 100644 --- a/model/krb.go +++ b/internal/auth/krb.go @@ -1,4 +1,4 @@ -package model +package auth import ( "bytes" diff --git a/model/ldap.go b/internal/auth/ldap.go similarity index 78% rename from model/ldap.go rename to internal/auth/ldap.go index 4f77762b519a0f4a6ef87e76e86f9560e3e42714..2264bc328f90a4fc0923feeb73c50f19428161c9 100644 --- a/model/ldap.go +++ b/internal/auth/ldap.go @@ -1,4 +1,4 @@ -package model +package auth import ( "fmt" @@ -8,11 +8,16 @@ import ( "strings" "github.com/go-ldap/ldap/v3" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) const ( PASSWD_PATH = "/etc/ldapscripts/ldapscripts.passwd" + MIN_GID = 1000 + MAX_GID = 5000 + MIN_UID = 1000 + MAX_UID = 30000 ) // stablishes a connection to LDAP @@ -33,7 +38,7 @@ func ConnLDAP() (*ldap.Conn, error) { err = l.Bind("cn=admin,dc=c3local", password) if err != nil { l.Close() - return nil, fmt.Errorf("Failed to bind with credentials: %v", err) + return nil, fmt.Errorf("Failed to bind with credentials: %w", err) } return l, nil @@ -53,7 +58,7 @@ func getPasswordLDAP(path string) (string, error) { } // generates a LDAP request and adds the user to LDAP -func (u *User) AddToLDAP(l *ldap.Conn) error { +func AddUserToLDAP(l *ldap.Conn, u *user.User) error { req := ldap.NewAddRequest(u.DN, nil) req.Attribute("uid", []string{u.UID}) @@ -67,7 +72,7 @@ func (u *User) AddToLDAP(l *ldap.Conn) error { err := l.Add(req) if err != nil { - return fmt.Errorf("Failed to add user %s to LDAP: %v", u.UID, err) + return fmt.Errorf("Failed to add user %s to LDAP: %w", u.UID, err) } err = AddToGroupLDAP(l, u.UID, u.GID) @@ -78,13 +83,7 @@ func (u *User) AddToLDAP(l *ldap.Conn) error { return nil } -func DelFromLDAP(UID string) error { - l, err := ConnLDAP() - if err != nil { - return err - } - defer l.Close() - +func DelFromLDAP(l *ldap.Conn, UID string) error { // search for all groups the user is a member of searchReq := ldap.NewSearchRequest( "ou=grupos,dc=c3local", @@ -119,7 +118,7 @@ func DelFromLDAP(UID string) error { return nil } -func LDAPCreateGroup(conn *ldap.Conn, GID, GIDNumber string) error { +func CreateGroupLDAP(l *ldap.Conn, GID, GIDNumber string) error { // contruct dn dn := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID) @@ -131,55 +130,54 @@ func LDAPCreateGroup(conn *ldap.Conn, GID, GIDNumber string) error { req.Attribute("gidNumber", []string{GIDNumber}) // create - if err := conn.Add(req); err != nil { + if err := l.Add(req); err != nil { return fmt.Errorf("failed to add group: %w", err) } return nil } -func LDAPDeleteGroup(conn *ldap.Conn, GID string) error { +func DeleteGroupLDAP(l *ldap.Conn, GID string) error { // construct dn dn := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID) delReq := ldap.NewDelRequest(dn, []ldap.Control{}) - if err := conn.Del(delReq); err != nil { + if err := l.Del(delReq); err != nil { return fmt.Errorf("failed to delete group: %w", err) } return nil } -func DelFromGroupLDAP(l *ldap.Conn, userUID, GID string) error { +func AddToGroupLDAP(l *ldap.Conn, UID, GID string) error { groupDN := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID) modReq := ldap.NewModifyRequest(groupDN, nil) - modReq.Delete("memberUid", []string{userUID}) + modReq.Add("memberUid", []string{UID}) err := l.Modify(modReq) - if err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchAttribute) { - return fmt.Errorf("Failed to remove user %s from group %s: %v", userUID, groupDN, err) + if err != nil { + return fmt.Errorf("Failed to add user %s to group %s: %v", UID, GID, err) } return nil } -// adds a user to a group in LDAP -func AddToGroupLDAP(l *ldap.Conn, UID, GID string) error { +func DelFromGroupLDAP(l *ldap.Conn, userUID, GID string) error { groupDN := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID) modReq := ldap.NewModifyRequest(groupDN, nil) - modReq.Add("memberUid", []string{UID}) + modReq.Delete("memberUid", []string{userUID}) err := l.Modify(modReq) - if err != nil { - return fmt.Errorf("Failed to add user %s to group %s: %v", UID, GID, err) + if err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchAttribute) { + return fmt.Errorf("Failed to remove user %s from group %s: %v", userUID, groupDN, err) } return nil } -func (u *User) ChangeBaseGroupLDAP(l *ldap.Conn, newGID string) error { - req := ldap.NewModifyRequest(u.DN, []ldap.Control{}) +func ChangeBaseGroupLDAP(l *ldap.Conn, DN, newGID string) error { + req := ldap.NewModifyRequest(DN, []ldap.Control{}) req.Replace("gidNumber", []string{newGID}) if err := l.Modify(req); err != nil { @@ -189,8 +187,7 @@ func (u *User) ChangeBaseGroupLDAP(l *ldap.Conn, newGID string) error { return nil } -func GetAllUsersLDAP(l *ldap.Conn) ([]User, error) { - var users []User +func GetAllUsersLDAP(l *ldap.Conn) (*ldap.SearchResult, error) { // create the LDAP search request req := ldap.NewSearchRequest( @@ -209,49 +206,10 @@ func GetAllUsersLDAP(l *ldap.Conn) ([]User, error) { sr, err := l.Search(req) if err != nil { err = fmt.Errorf("Failed to fetch users from LDAP: %v", err) - return nil, err - } - - // get all the groups and ids - groups, err := GetAllGroupsLDAP(l) - if err != nil { - err = fmt.Errorf("Failed to fetch groups and gids from LDAP: %v", err) - return nil, err - } - - // iterate over the search results - for _, entry := range sr.Entries { - shell := entry.GetAttributeValue("loginShell") - - user := User{ - DN: entry.DN, - UID: entry.GetAttributeValue("uid"), - Name: entry.GetAttributeValue("cn"), - Shell: shell, - UIDNumber: entry.GetAttributeValue("uidNumber"), - GIDNumber: entry.GetAttributeValue("gidNumber"), - Homedir: entry.GetAttributeValue("homeDirectory"), - Gecos: entry.GetAttributeValue("gecos"), - Status: utils.IfThenElse(shell == "/bin/bash", "Active", "Blocked"), - } - - user.GID = groups[user.GIDNumber] - - gidNumber := user.GIDNumber - - // safe assignment :) - groupName, exists := groups[gidNumber] - if !exists { - fmt.Printf("WARNING: no group found for GIDNumber %s, user %s. Continuing...", gidNumber, user.UID) - } else { - user.GID = groupName - } - - user.ParseGecos() - users = append(users, user) + return sr, err } - return users, nil + return sr, nil } func GetAllGroupsLDAP(l *ldap.Conn) (map[string]string, error) { @@ -324,6 +282,21 @@ func GetAllUIDsLDAP(l *ldap.Conn) ([]int, error) { return uidNumbers, nil } +// finds next available uidNumber +func GetNewUIDNumLDAP(l *ldap.Conn) (string, error) { + uids, err := GetAllUIDsLDAP(l) + if err != nil { + return "", err + } + + res, err := utils.GetMEX(uids, MIN_UID, MAX_UID) + if err != nil { + return "", fmt.Errorf("Error generating new uidNumber: %w", err) + } + + return strconv.Itoa(res), nil +} + func GetAllGIDsLDAP(l *ldap.Conn) ([]int, error) { var gidNumbers []int @@ -361,3 +334,18 @@ func GetAllGIDsLDAP(l *ldap.Conn) ([]int, error) { return gidNumbers, nil } + +// finds next available gidNumber +func GetNewGIDNumLDAP(l *ldap.Conn) (string, error) { + gids, err := GetAllGIDsLDAP(l) + if err != nil { + return "", err + } + + res, err := utils.GetMEX(gids, MIN_GID, MAX_GID) + if err != nil { + return "", fmt.Errorf("Error generating new gidNumber: %w", err) + } + + return strconv.Itoa(res), nil +} diff --git a/model/ltype.go b/internal/entities/ltype/ltype.go similarity index 98% rename from model/ltype.go rename to internal/entities/ltype/ltype.go index 02df3c12ebea1457986b43397b6c4ea01b956a5f..6e5a2fe83dfafaa0a75d2fc06112d34a147ccd0c 100644 --- a/model/ltype.go +++ b/internal/entities/ltype/ltype.go @@ -1,4 +1,4 @@ -package model +package ltype import "strings" diff --git a/model/opts.go b/internal/entities/opts/opts.go similarity index 99% rename from model/opts.go rename to internal/entities/opts/opts.go index b399d8b9deae9bdb862ecfc014a383a18b8aa8fa..42777bf8b254d85c266864ca8ebf7083d32968a2 100644 --- a/model/opts.go +++ b/internal/entities/opts/opts.go @@ -1,4 +1,4 @@ -package model +package opts import ( "fmt" diff --git a/internal/entities/user/user.go b/internal/entities/user/user.go new file mode 100644 index 0000000000000000000000000000000000000000..0e44861593213c3dc94cdcd4c955fe73b9720ae5 --- /dev/null +++ b/internal/entities/user/user.go @@ -0,0 +1,258 @@ +package user + +import ( + "errors" + "fmt" + + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/ltype" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" +) + +const ( + STD_SHELL = "/bin/bash" + STD_STATUS = "Active" +) + +var ( + ErrAlreadySet = errors.New("Field is already set, won't overwrite") + ErrMissingRequiredFieldUID = errors.New("Missing argument UID uppon generating field") + ErrMissingRequiredFieldGID = errors.New("Missing argument GID uppon generating field") + ErrMissingRequiredFieldName = errors.New("Missing argument Name uppon generating field") +) + +type User struct { + DN string + GRR string + UID string + GID string + Name string + Resp string + Ltype ltype.LoginType + Gecos string + Shell string + Status string + Webdir string + Expiry string + Homedir string + Password string + Nobackup string + GIDNumber string + UIDNumber string +} + +func (u *User) GenGRR() error { + if u.GRR == "" { + u.GRR = "_" + return nil + } + return ErrAlreadySet +} + +func (u *User) GenName() error { + if u.Name == "" { + u.Name = "_" + return nil + } + return ErrAlreadySet +} + +func (u *User) GenResp() error { + if u.Resp == "" { + u.Resp = "_" + return nil + } + return ErrAlreadySet +} + +func (u *User) GenExpiry() error { + if u.Expiry == "" { + u.Expiry = "_" + return nil + } + return ErrAlreadySet +} + +func (u *User) GenShell() error { + if u.Shell == "" { + u.Shell = STD_SHELL + return nil + } + return ErrAlreadySet +} + +func (u *User) GenStatus() error { + if u.Status == "" { + u.Status = STD_STATUS + return nil + } + return ErrAlreadySet +} + +func (u *User) GenDN() error { + if u.UID == "" || u.UID == "_" { + return ErrMissingRequiredFieldUID + } + u.DN = "uid=" + u.UID + ",ou=usuarios,dc=c3local" + return nil +} + +func (u *User) GenWebPath() (bool, error) { + var exists bool + + if u.UID == "" || u.UID == "_" { + return false, ErrMissingRequiredFieldUID + } + + if u.Webdir == "" || u.Webdir == "_" { + u.Webdir, exists = utils.GenDirPath("/home/html/inf", "", u.UID) + return exists, nil + } + + return false, ErrAlreadySet +} + +func (u *User) GenHomePath() (bool, error) { + var exists bool + + if u.UID == "" || u.UID == "_" { + return false, ErrMissingRequiredFieldUID + } + + if u.GID == "" || u.GID == "_" { + return false, ErrMissingRequiredFieldGID + } + + if u.Homedir == "" || u.Homedir == "_" { + u.Homedir, exists = utils.GenDirPath("/home", u.GID, u.UID) + return exists, nil + } + + return false, ErrAlreadySet +} + +func (u *User) GenNobackupPath() (bool, error) { + var exists bool + + if u.UID == "" || u.UID == "_" { + return false, ErrMissingRequiredFieldUID + } + + if u.GID == "" || u.GID == "_" { + return false, ErrMissingRequiredFieldGID + } + + if u.Nobackup == "" || u.Nobackup == "_" { + u.Nobackup, exists = utils.GenDirPath("/nobackup", u.GID, u.UID) + return exists, nil + } + + return false, ErrAlreadySet +} + +func (u *User) GenGecos() { + u.Gecos = u.Name + "," + u.Gecos += u.GRR + "," + u.Gecos += u.Resp + "," + u.Gecos += u.Expiry + "," + u.Gecos += u.Status + "," + u.Gecos += u.Ltype.String() + "," + u.Gecos += u.Webdir + "," + u.Gecos += u.Nobackup +} + +func (u *User) GenUID(variance int) string { + parts := utils.FormatName(u.Name) + if len(parts) == 0 || u.Ltype == ltype.LoginTypeUnknown { + return "_" + } + + partPrefixLen := make([]int, len(parts)) + if u.Ltype == ltype.Initials { + // the first letter of each part must appear + for i := 0; i < len(parts); i++ { + partPrefixLen[i] = 1 + } + } else if u.Ltype == ltype.FirstName { + // the first name (part) must appear + partPrefixLen[0] = len(parts[0]) + } else { + // the first letter of each part must appear + for i := 0; i < len(parts); i++ { + partPrefixLen[i] = 1 + } + // last part aswell + partPrefixLen[len(parts)-1] = len(parts[len(parts)-1]) + } + + partPrefixIx := 0 + for i := 0; i < variance; i++ { + ok := false + for k := 0; k < len(parts) && !ok; k++ { + if partPrefixLen[partPrefixIx] < len(parts[partPrefixIx]) { + partPrefixLen[partPrefixIx]++ + ok = true + } + partPrefixIx = (partPrefixIx + 1) % len(parts) + } + if !ok { + // it's joever, from now on nothing happens, quit :D + break + } + } + + // contruct the login with the given legths + login := "" + for i := 0; i < len(parts); i++ { + login += parts[i][:partPrefixLen[i]] + } + if login == "" { + return "_" + } + + if u.Ltype == ltype.Initials { + login += u.GRR[2:4] + } + return login +} + +func (u *User) ToString() string { + return fmt.Sprintf(`User: + Name: %v + Login: %v + GRR: %v + Group: %v + Shell: %v + Home: %v + Webdir: %v + Nobkp: %v + Status: %v + Resp: %v + Expiry: %v`, + u.Name, u.UID, u.GRR, u.GID, u.Shell, u.Homedir, u.Webdir, + u.Nobackup, u.Status, u.Resp, u.Expiry) +} + +// useful for debugging :) +func (u *User) FullToString() string { + return fmt.Sprintf(`User: + DN: %v + GRR: %v + UID: %v + GID: %v + Name: %v + Resp: %v + Ltype: %v + Gecos: %v + Shell: %v + Status: %v + Webdir: %v + Expiry: %v + Homedir: %v + Password: %v + Nobackup: %v + GIDNumber: %v + UIDNumber: %v`, + u.DN, u.GRR, u.UID, u.GID, u.Name, u.Resp, u.Ltype, u.Gecos, + u.Shell, u.Status, u.Webdir, u.Expiry, u.Homedir, u.Password, + u.Nobackup, u.GIDNumber, u.UIDNumber) +} diff --git a/internal/entities/user/user_test.go b/internal/entities/user/user_test.go new file mode 100644 index 0000000000000000000000000000000000000000..345a1a8301f9c7b4a0c45181f950e37cab1d1cc6 --- /dev/null +++ b/internal/entities/user/user_test.go @@ -0,0 +1,124 @@ +package user + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/ltype" +) + +func TestGenLogin(t *testing.T) { + userTable := []struct { + name string + grr string + ltype ltype.LoginType + variance int + expected string + }{ + {"", "20241982", ltype.Initials, 0, "_"}, + {"", "20241982", ltype.Initials, 1, "_"}, + {"", "20241982", ltype.Initials, 2, "_"}, + {"", "20241982", ltype.Initials, 3, "_"}, + + {"", "20241982", ltype.FirstName, 0, "_"}, + {"", "20241982", ltype.FirstName, 1, "_"}, + {"", "20241982", ltype.FirstName, 2, "_"}, + {"", "20241982", ltype.FirstName, 3, "_"}, + + {"", "20241982", ltype.LastName, 0, "_"}, + {"", "20241982", ltype.LastName, 1, "_"}, + {"", "20241982", ltype.LastName, 2, "_"}, + {"", "20241982", ltype.LastName, 3, "_"}, + + {"de", "20241982", ltype.Initials, 0, "_"}, + {"de", "20241982", ltype.Initials, 1, "_"}, + {"de", "20241982", ltype.Initials, 2, "_"}, + {"de", "20241982", ltype.Initials, 3, "_"}, + + {"de", "20241982", ltype.FirstName, 0, "_"}, + {"de", "20241982", ltype.FirstName, 1, "_"}, + {"de", "20241982", ltype.FirstName, 2, "_"}, + {"de", "20241982", ltype.FirstName, 3, "_"}, + + {"de", "20241982", ltype.LastName, 0, "_"}, + {"de", "20241982", ltype.LastName, 1, "_"}, + {"de", "20241982", ltype.LastName, 2, "_"}, + {"de", "20241982", ltype.LastName, 3, "_"}, + + {"da de", "20241982", ltype.Initials, 0, "_"}, + {"da de", "20241982", ltype.Initials, 1, "_"}, + {"da de", "20241982", ltype.Initials, 2, "_"}, + {"da de", "20241982", ltype.Initials, 3, "_"}, + + {"da de", "20241982", ltype.FirstName, 0, "_"}, + {"da de", "20241982", ltype.FirstName, 1, "_"}, + {"da de", "20241982", ltype.FirstName, 2, "_"}, + {"da de", "20241982", ltype.FirstName, 3, "_"}, + + {"da de", "20241982", ltype.LastName, 0, "_"}, + {"da de", "20241982", ltype.LastName, 1, "_"}, + {"da de", "20241982", ltype.LastName, 2, "_"}, + {"da de", "20241982", ltype.LastName, 3, "_"}, + + {"Fabiano", "20241982", ltype.Initials, 0, "f24"}, + {"Fabiano", "20241982", ltype.Initials, 1, "fa24"}, + {"Fabiano", "20241982", ltype.Initials, 2, "fab24"}, + {"Fabiano", "20241982", ltype.Initials, 3, "fabi24"}, + + {"Fabiano", "20241982", ltype.FirstName, 0, "fabiano"}, + {"Fabiano", "20241982", ltype.FirstName, 1, "fabiano"}, + {"Fabiano", "20241982", ltype.FirstName, 2, "fabiano"}, + {"Fabiano", "20241982", ltype.FirstName, 3, "fabiano"}, + + {"Fabiano", "20241982", ltype.LastName, 0, "fabiano"}, + {"Fabiano", "20241982", ltype.LastName, 1, "fabiano"}, + {"Fabiano", "20241982", ltype.LastName, 2, "fabiano"}, + {"Fabiano", "20241982", ltype.LastName, 3, "fabiano"}, + + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 0, "faps24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 1, "faaps24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 2, "faanps24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 3, "faanpes24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 14, "fabiaantunperesouz24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 18, "fabianantunepereisouza24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 19, "fabianantunepereirsouza24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 20, "fabianoantunepereirsouza24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 21, "fabianoantunespereirsouza24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 22, "fabianoantunespereirasouza24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 40, "fabianoantunespereirasouza24"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.Initials, 50, "fabianoantunespereirasouza24"}, + + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 0, "fabiano"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 1, "fabianoa"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 2, "fabianoap"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 3, "fabianoaps"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 4, "fabianoanps"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 16, "fabianoantunepereisouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 17, "fabianoantunepereirsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 18, "fabianoantunespereirsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 19, "fabianoantunespereirasouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 20, "fabianoantunespereirasouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.FirstName, 50, "fabianoantunespereirasouza"}, + + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 0, "fapsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 1, "faapsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 2, "faanpsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 3, "faanpesouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 15, "fabianantunepereirsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 16, "fabianoantunepereirsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 17, "fabianoantunespereirsouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 18, "fabianoantunespereirasouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 19, "fabianoantunespereirasouza"}, + {"Fabiano Antunes Pereira de Souza", "20241982", ltype.LastName, 100, "fabianoantunespereirasouza"}, + } + + for _, u := range userTable { + user := &User{ + Name: u.name, + GRR: u.grr, + Ltype: u.ltype, + } + login := user.GenUID(u.variance) + assert.Equal(t, u.expected, login) + } +} diff --git a/model/user.go b/internal/manage/manage.go similarity index 56% rename from model/user.go rename to internal/manage/manage.go index a1f45104d6ee9dbcdb9e041cc9faa5c08e1f7f03..f1c9d853f24fdc5252a2524c13ae82de4b7c6dd6 100644 --- a/model/user.go +++ b/internal/manage/manage.go @@ -1,4 +1,4 @@ -package model +package manage import ( "fmt" @@ -10,17 +10,15 @@ import ( "time" "github.com/go-ldap/ldap/v3" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/auth" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) const ( - MIN_UID = 1000 // up to 1000 are system users, 0 is root - MAX_UID = 30000 // max UIDNumber permitted - MIN_GID = 1000 // up to 1000 are system users, 0 is root - MAX_GID = 5000 // max GIDNumber permitted + STD_PERM = "0700" // default user dir permission + BLK_PERM = "0000" // user dir permission if blocked MAX_VARIANCE = 60 // stops iteration at some point - 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 NUM_GECOS_FIELDS = 8 ) @@ -38,53 +36,33 @@ var ( GID_HAS_WEB = [...]string{"bcc", "ibm", "ppginf", "c3sl", "prof", "especial"} ) -type User struct { - DN string - GRR string - UID string - GID string - Name string - Resp string - Ltype LoginType - Gecos string - Shell string - Status string - Webdir string - Expiry string - Homedir string - Password string - Nobackup string - GIDNumber string - UIDNumber string -} - -func (u *User) Create(l *ldap.Conn) error { +func CreateUser(l *ldap.Conn, u *user.User) error { success := false defer func() { if !success { fmt.Printf("Error found, deleting user...\n") - _ = DelKRBPrincipal(u.UID) - _ = DelFromLDAP(u.UID) + _ = auth.DelKRBPrincipal(u.UID) + _ = auth.DelFromLDAP(l, u.UID) } }() - err := u.AddToLDAP(l) + err := auth.AddUserToLDAP(l, u) if err != nil { return err } - err = CreateKRBPrincipal(u.UID) + err = auth.CreateKRBPrincipal(u.UID) if err != nil { return err } - err = ModKRBPassword(u.UID, u.Password) + err = auth.ModKRBPassword(u.UID, u.Password) if err != nil { return err } - err = u.CreateDirs() + err = CreateUserDirs(u) if err != nil { return err } @@ -95,8 +73,8 @@ func (u *User) Create(l *ldap.Conn) error { return nil } -func Search(users []User, ig, ex bool, l, g, n, r, s, h string) []User { - return utils.Filter(users, func(u User) bool { +func Search(users []user.User, ig, ex bool, l, g, n, r, s, h string) []user.User { + return utils.Filter(users, func(u user.User) bool { matches := func(src, target string) bool { if ig { src, target = strings.ToLower(src), strings.ToLower(target) @@ -118,29 +96,29 @@ func Search(users []User, ig, ex bool, l, g, n, r, s, h string) []User { } // searches for the specified login string, there can only be one >:) -func Locate(l *ldap.Conn, login string) (User, error) { - var u User - users, err := GetAllUsersLDAP(l) +func Locate(l *ldap.Conn, login string) (*user.User, error) { + var u user.User + users, err := GetAllUsers(l) if err != nil { - return u, err + return &u, err } if !LoginExists(users, login) { - return u, fmt.Errorf("No such user!") + return &u, fmt.Errorf("No such user!") } filter := Search(users, false, true, login, "", "", "", "", "") if len(filter) != 1 { - return u, fmt.Errorf(`More than one user matched the login given. + return &u, fmt.Errorf(`More than one user matched the login given. search made: "useradm user show -l %v -e"`, login) } u = filter[0] - return u, nil + return &u, nil } -func (u *User) Block() error { - err := KRBRandKey(u.UID) // new password, no one will know +func Block(u *user.User) error { + err := auth.KRBRandKey(u.UID) // new password, no one will know if err != nil { return err } @@ -155,7 +133,7 @@ func (u *User) Block() error { return fmt.Errorf("Failed to set permissions: %w", err) } - l, err := ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } @@ -172,7 +150,7 @@ func (u *User) Block() error { return nil } -func (u *User) Unblock(pass string) error { +func Unblock(u *user.User, pass string) error { cmd := exec.Command("chmod", "0700", u.Homedir) if err := cmd.Run(); err != nil { return fmt.Errorf("Failed to set permissions: %w", err) @@ -183,7 +161,7 @@ func (u *User) Unblock(pass string) error { return fmt.Errorf("Failed to set permissions: %w", err) } - l, err := ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } @@ -200,7 +178,7 @@ func (u *User) Unblock(pass string) error { pass = utils.GenPassword() } - err = ModKRBPassword(u.UID, pass) + err = auth.ModKRBPassword(u.UID, pass) if err != nil { return err } @@ -210,62 +188,11 @@ func (u *User) Unblock(pass string) error { return nil } -// DN formatting -func (u *User) SetDN(UID string) { - u.DN = "uid=" + UID + ",ou=usuarios,dc=c3local" -} - -// finds next available uidNumber (MEX from uid group) -func (u *User) GetNewUIDNumber(l *ldap.Conn) error { - uids, err := GetAllUIDsLDAP(l) - if err != nil { - return err - } - - candidate := MIN_UID - for _, uid := range uids { - if uid == candidate { // check if taken - candidate++ - } else if uid > candidate { // found a gap - break - } - } - - if candidate > MAX_UID { - return fmt.Errorf("No more available UID numbers") - } - - u.UIDNumber = strconv.Itoa(candidate) - return nil -} - -func GetNewGIDNumber(l *ldap.Conn) (string, error) { - uids, err := GetAllGIDsLDAP(l) - if err != nil { - return "", err - } - - candidate := MIN_GID - for _, uid := range uids { - if uid == candidate { // check if taken - candidate++ - } else if uid > candidate { // found a gap - break - } - } - - if candidate > MAX_GID { - return "", fmt.Errorf("No more available GID numbers") - } - - return strconv.Itoa(candidate), nil -} - // creates the login, if it already exists try again with variance -func (u *User) GenUniqueUID(users []User) error { +func GenUniqueUID(users []user.User, u *user.User) error { used, variance := true, 0 for used { - u.UID = u.genLogin(variance) + u.UID = u.GenUID(variance) used = LoginExists(users, u.UID) || utils.MailAliasExists(u.UID) @@ -277,72 +204,6 @@ func (u *User) GenUniqueUID(users []User) error { return nil } -func (u *User) genLogin(variance int) string { - parts := utils.FormatName(u.Name) - if len(parts) == 0 || u.Ltype == LoginTypeUnknown { - return "_" - } - - partPrefixLen := make([]int, len(parts)) - if u.Ltype == Initials { - // the first letter of each part must appear - for i := 0; i < len(parts); i++ { - partPrefixLen[i] = 1 - } - } else if u.Ltype == FirstName { - // the first name (part) must appear - partPrefixLen[0] = len(parts[0]) - } else { - // the first letter of each part must appear - for i := 0; i < len(parts); i++ { - partPrefixLen[i] = 1 - } - // last part aswell - partPrefixLen[len(parts)-1] = len(parts[len(parts)-1]) - } - - partPrefixIx := 0 - for i := 0; i < variance; i++ { - ok := false - for k := 0; k < len(parts) && !ok; k++ { - if partPrefixLen[partPrefixIx] < len(parts[partPrefixIx]) { - partPrefixLen[partPrefixIx]++ - ok = true - } - partPrefixIx = (partPrefixIx + 1) % len(parts) - } - if !ok { - // it's joever, from now on nothing happens, quit :D - break - } - } - - // contruct the login with the given legths - login := "" - for i := 0; i < len(parts); i++ { - login += parts[i][:partPrefixLen[i]] - } - if login == "" { - return "_" - } - - if u.Ltype == Initials { - login += u.GRR[2:4] - } - return login -} - -func (u *User) GenGecos() { - u.Gecos = u.Name + "," - u.Gecos += u.GRR + "," - u.Gecos += u.Resp + "," - u.Gecos += u.Expiry + "," - u.Gecos += u.Status + "," - u.Gecos += u.Ltype.String() + "," - u.Gecos += u.Webdir + "," - u.Gecos += u.Nobackup -} - // Gecos Fields: // 0. users full name // 1. users grr @@ -353,7 +214,7 @@ func (u *User) GenGecos() { // 6. users webdir // 7. users nobackup dir // 8. to be continued... -func (u *User) ParseGecos() { +func ParseGecos(u *user.User) { result := [NUM_GECOS_FIELDS]string{0: "_"} for i := range result { result[i] = "_" @@ -378,66 +239,24 @@ func (u *User) ParseGecos() { u.Nobackup = result[7] } -func (u *User) ToString() string { - return fmt.Sprintf(`User: - Name: %v - Login: %v - GRR: %v - Group: %v - Shell: %v - Home: %v - Webdir: %v - Nobkp: %v - Status: %v - Resp: %v - Expiry: %v`, - u.Name, u.UID, u.GRR, u.GID, u.Shell, u.Homedir, u.Webdir, - u.Nobackup, u.Status, u.Resp, u.Expiry) +func LoginExists(users []user.User, login string) bool { + return utils.Exists(users, func(u user.User) bool { return u.UID == login }) } -// useful for debugging :) -func (u *User) FullToString() string { - return fmt.Sprintf(`User: - DN: %v - GRR: %v - UID: %v - GID: %v - Name: %v - Resp: %v - Ltype: %v - Gecos: %v - Shell: %v - Status: %v - Webdir: %v - Expiry: %v - Homedir: %v - Password: %v - Nobackup: %v - GIDNumber: %v - UIDNumber: %v`, - u.DN, u.GRR, u.UID, u.GID, u.Name, u.Resp, u.Ltype, u.Gecos, - u.Shell, u.Status, u.Webdir, u.Expiry, u.Homedir, u.Password, - u.Nobackup, u.GIDNumber, u.UIDNumber) -} - -func LoginExists(users []User, login string) bool { - return utils.Exists(users, func(u User) bool { return u.UID == login }) -} - -func NameExists(users []User, name string) bool { - return utils.Exists(users, func(u User) bool { +func NameExists(users []user.User, name string) bool { + return utils.Exists(users, func(u user.User) bool { return strings.ToLower(u.Name) == strings.ToLower(name) }) } // accepts GRRs in the format "GRRXXXXXXXX" aswell as just 8 digit number -func GRRExists(users []User, grr string) bool { - return utils.Exists(users, func(u User) bool { return u.GRR == grr }) || - utils.Exists(users, func(u User) bool { return u.GRR == "GRR"+grr }) +func GRRExists(users []user.User, grr string) bool { + return utils.Exists(users, func(u user.User) bool { return u.GRR == grr }) || + utils.Exists(users, func(u user.User) bool { return u.GRR == "GRR"+grr }) } // moves dirs to their respective trash dir -func (u *User) DirsToTrash() error { +func MoveDirsToTrash(u *user.User) error { err := utils.MoveAndChown(u.Homedir, HOME_TRASH, "nobody", "nogroup") if err != nil { return err @@ -458,7 +277,7 @@ func (u *User) DirsToTrash() error { return nil } -func (u *User) CreateDirs() error { +func CreateUserDirs(u *user.User) error { success := false defer func() { @@ -469,15 +288,15 @@ func (u *User) CreateDirs() error { } }() - if err := u.CreateHome(u.Homedir); err != nil { + if err := CreateHome(u, u.Homedir); err != nil { return err } - if err := u.CreateHome(u.Nobackup); err != nil { + if err := CreateHome(u, u.Nobackup); err != nil { return err } - if err := u.CreateWeb(); err != nil { + if err := CreateWeb(u); err != nil { return err } @@ -485,12 +304,7 @@ func (u *User) CreateDirs() error { return nil } -func (u *User) CreateHome(homeDir string) error { - perm := DEF_PERMISSION - if u.Status == "Blocked" { - perm = BLK_PERMISSION - } - +func CreateHome(u *user.User, homeDir string) error { // create directory cmd := exec.Command("mkdir", "-p", homeDir) cmd.Stdout = nil @@ -506,7 +320,7 @@ func (u *User) CreateHome(homeDir string) error { } // change permissions - cmd = exec.Command("chmod", perm, homeDir) + cmd = exec.Command("chmod", STD_PERM, homeDir) if err := cmd.Run(); err != nil { return fmt.Errorf("Failed to set permissions: %w", err) } @@ -520,7 +334,7 @@ func (u *User) CreateHome(homeDir string) error { return nil } -func (u *User) CreateWeb() error { +func CreateWeb(u *user.User) error { success := false if u.Webdir == "_" { @@ -528,11 +342,6 @@ func (u *User) CreateWeb() error { return nil } - perm := DEF_PERMISSION - if u.Status == "Blocked" { - perm = BLK_PERMISSION - } - defer func() { if !success { _ = os.RemoveAll(u.Webdir) @@ -560,7 +369,7 @@ func (u *User) CreateWeb() error { } // change permissions - cmd = exec.Command("chmod", perm, u.Webdir) + cmd = exec.Command("chmod", STD_PERM, u.Webdir) if err := cmd.Run(); err != nil { return fmt.Errorf("Failed to set permissions: %w", err) } @@ -584,48 +393,41 @@ func (u *User) CreateWeb() error { // needs the name, group, ltype. // will derivate the rest of the information. // if it can't infer the field, will set to "_" -func (u *User) GenMissingFields() error { +func GenMissingFields(u *user.User) error { var err error - l, err := ConnLDAP() + l, err := auth.ConnLDAP() if err != nil { return err } defer l.Close() if u.UID == "" { - users, err := GetAllUsersLDAP(l) + users, err := GetAllUsers(l) if err != nil { return err } - err = u.GenUniqueUID(users) + err = GenUniqueUID(users, u) if err != nil { return err } } - if u.DN == "" { - u.SetDN(u.UID) + err = u.GenDN() + if err != nil { + return err } - if u.GRR == "" { - u.GRR = "_" - } + _ = u.GenGRR() - if u.Expiry == "" { - u.Expiry = "_" - } + _ = u.GenExpiry() - if u.Status == "" { - u.Status = "Active" - } + _ = u.GenStatus() - if u.Shell == "" { - u.Shell = "/bin/bash" - } + _ = u.GenShell() - groups, err := GetAllGroupsLDAP(l) + groups, err := auth.GetAllGroupsLDAP(l) if err != nil { return err } @@ -650,19 +452,68 @@ func (u *User) GenMissingFields() error { u.Webdir = "_" for _, e := range GID_HAS_WEB { if e == u.GID { - u.Webdir, _ = utils.GenDirPath("/home/html/inf", "", u.UID) + u.GenWebPath() break } } if u.UIDNumber == "" { - err = u.GetNewUIDNumber(l) + uidNum, err := auth.GetNewUIDNumLDAP(l) if err != nil { return fmt.Errorf("Failed to generate new UIDNumber for user: %v", err) } + u.UIDNumber = uidNum } u.GenGecos() return nil } + +func GetAllUsers(l *ldap.Conn) ([]user.User, error) { + var users []user.User + + res, err := auth.GetAllUsersLDAP(l) + if err != nil { + return nil, err + } + + groups, err := auth.GetAllGroupsLDAP(l) + if err != nil { + return nil, err + } + + // iterate over the search results + for _, entry := range res.Entries { + shell := entry.GetAttributeValue("loginShell") + + u := user.User{ + Shell: shell, + DN: entry.DN, + Name: entry.GetAttributeValue("cn"), + UID: entry.GetAttributeValue("uid"), + Gecos: entry.GetAttributeValue("gecos"), + UIDNumber: entry.GetAttributeValue("uidNumber"), + GIDNumber: entry.GetAttributeValue("gidNumber"), + Homedir: entry.GetAttributeValue("homeDirectory"), + Status: utils.IfThenElse(shell == "/bin/false", "Blocked", "Active"), + } + + u.GID = groups[u.GIDNumber] + + gidNumber := u.GIDNumber + + // safe assignment :) + groupName, exists := groups[gidNumber] + if !exists { + fmt.Printf("WARNING: no group found for GIDNumber %s, user %s. Continuing...", gidNumber, u.UID) + } else { + u.GID = groupName + } + + ParseGecos(&u) + users = append(users, u) + } + + return users, nil +} diff --git a/validate/validate.go b/internal/validate/validate.go similarity index 78% rename from validate/validate.go rename to internal/validate/validate.go index 1ab8aa37548cd20fcebfd6b42d9927c8ed18605d..9ed81a5fc7847f9078e3d92ccf39da641ac90867 100644 --- a/validate/validate.go +++ b/internal/validate/validate.go @@ -5,8 +5,9 @@ import ( "regexp" "strings" - "gitlab.c3sl.ufpr.br/tss24/useradm/model" - "gitlab.c3sl.ufpr.br/tss24/useradm/utils" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/entities/user" + "gitlab.c3sl.ufpr.br/tss24/useradm/internal/manage" + "gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils" ) func Expiry(expiry string) error { @@ -30,7 +31,7 @@ func Status(status string) error { return nil } -func GRR(users []model.User, grr string) error { +func GRR(users []user.User, grr string) error { // OK if empty, only "ini" login type requires it and we check :) if grr == "_" { return nil @@ -42,7 +43,7 @@ func GRR(users []model.User, grr string) error { return err } - if model.GRRExists(users, grr) { + if manage.GRRExists(users, grr) { err := fmt.Errorf(`The informed GRR already exists in LDAP database Note: To search for the account use "useradm user show -r %s"`, grr) return err @@ -63,8 +64,8 @@ func GID(groups map[string]string, group string) error { return err } -func UID(users []model.User, login string) error { - res := model.Search(users, false, true, login, "", "", "", "", "") +func UID(users []user.User, login string) error { + res := manage.Search(users, false, true, login, "", "", "", "", "") if len(res) != 0 { return fmt.Errorf(`The informed Login already exists in LDAP database Note: To search for the account use "useradm user show -l %s"`, login) diff --git a/model/user_test.go b/model/user_test.go deleted file mode 100644 index a7d4b4a0849ad69cc1fb9b6efc7116b4e4d4be77..0000000000000000000000000000000000000000 --- a/model/user_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package model - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestGenLogin(t *testing.T) { - userTable := []struct { - name string - grr string - ltype LoginType - variance int - expected string - }{ - {"", "20241982", Initials, 0, "_"}, - {"", "20241982", Initials, 1, "_"}, - {"", "20241982", Initials, 2, "_"}, - {"", "20241982", Initials, 3, "_"}, - - {"", "20241982", FirstName, 0, "_"}, - {"", "20241982", FirstName, 1, "_"}, - {"", "20241982", FirstName, 2, "_"}, - {"", "20241982", FirstName, 3, "_"}, - - {"", "20241982", LastName, 0, "_"}, - {"", "20241982", LastName, 1, "_"}, - {"", "20241982", LastName, 2, "_"}, - {"", "20241982", LastName, 3, "_"}, - - {"de", "20241982", Initials, 0, "_"}, - {"de", "20241982", Initials, 1, "_"}, - {"de", "20241982", Initials, 2, "_"}, - {"de", "20241982", Initials, 3, "_"}, - - {"de", "20241982", FirstName, 0, "_"}, - {"de", "20241982", FirstName, 1, "_"}, - {"de", "20241982", FirstName, 2, "_"}, - {"de", "20241982", FirstName, 3, "_"}, - - {"de", "20241982", LastName, 0, "_"}, - {"de", "20241982", LastName, 1, "_"}, - {"de", "20241982", LastName, 2, "_"}, - {"de", "20241982", LastName, 3, "_"}, - - {"da de", "20241982", Initials, 0, "_"}, - {"da de", "20241982", Initials, 1, "_"}, - {"da de", "20241982", Initials, 2, "_"}, - {"da de", "20241982", Initials, 3, "_"}, - - {"da de", "20241982", FirstName, 0, "_"}, - {"da de", "20241982", FirstName, 1, "_"}, - {"da de", "20241982", FirstName, 2, "_"}, - {"da de", "20241982", FirstName, 3, "_"}, - - {"da de", "20241982", LastName, 0, "_"}, - {"da de", "20241982", LastName, 1, "_"}, - {"da de", "20241982", LastName, 2, "_"}, - {"da de", "20241982", LastName, 3, "_"}, - - {"Fabiano", "20241982", Initials, 0, "f24"}, - {"Fabiano", "20241982", Initials, 1, "fa24"}, - {"Fabiano", "20241982", Initials, 2, "fab24"}, - {"Fabiano", "20241982", Initials, 3, "fabi24"}, - - {"Fabiano", "20241982", FirstName, 0, "fabiano"}, - {"Fabiano", "20241982", FirstName, 1, "fabiano"}, - {"Fabiano", "20241982", FirstName, 2, "fabiano"}, - {"Fabiano", "20241982", FirstName, 3, "fabiano"}, - - {"Fabiano", "20241982", LastName, 0, "fabiano"}, - {"Fabiano", "20241982", LastName, 1, "fabiano"}, - {"Fabiano", "20241982", LastName, 2, "fabiano"}, - {"Fabiano", "20241982", LastName, 3, "fabiano"}, - - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 0, "faps24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 1, "faaps24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 2, "faanps24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 3, "faanpes24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 14, "fabiaantunperesouz24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 18, "fabianantunepereisouza24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 19, "fabianantunepereirsouza24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 20, "fabianoantunepereirsouza24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 21, "fabianoantunespereirsouza24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 22, "fabianoantunespereirasouza24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 40, "fabianoantunespereirasouza24"}, - {"Fabiano Antunes Pereira de Souza", "20241982", Initials, 50, "fabianoantunespereirasouza24"}, - - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 0, "fabiano"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 1, "fabianoa"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 2, "fabianoap"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 3, "fabianoaps"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 4, "fabianoanps"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 16, "fabianoantunepereisouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 17, "fabianoantunepereirsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 18, "fabianoantunespereirsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 19, "fabianoantunespereirasouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 20, "fabianoantunespereirasouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", FirstName, 50, "fabianoantunespereirasouza"}, - - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 0, "fapsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 1, "faapsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 2, "faanpsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 3, "faanpesouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 15, "fabianantunepereirsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 16, "fabianoantunepereirsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 17, "fabianoantunespereirsouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 18, "fabianoantunespereirasouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 19, "fabianoantunespereirasouza"}, - {"Fabiano Antunes Pereira de Souza", "20241982", LastName, 100, "fabianoantunespereirasouza"}, - } - - for _, u := range userTable { - user := &User{ - Name: u.name, - GRR: u.grr, - Ltype: u.ltype, - } - login := user.genLogin(u.variance) - assert.Equal(t, u.expected, login) - } -} diff --git a/utils/utils.go b/pkg/utils/utils.go similarity index 91% rename from utils/utils.go rename to pkg/utils/utils.go index a91653240c95a3a34407803f4344aadbc0f77f19..d38a3c4d4ed720f557a8980876a9aefb21b3a849 100644 --- a/utils/utils.go +++ b/pkg/utils/utils.go @@ -195,3 +195,26 @@ func IsValidDate(arr []string) bool { t := time.Date(fullYear, time.Month(mth), day, 0, 0, 0, 0, time.UTC) return t.Day() == day && t.Month() == time.Month(mth) } + +type Integer interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 +} + +func GetMEX[T Integer](set []T, minValue, maxValue T) (T, error) { + candidate := minValue + + for _, val := range set { + if val == candidate { + candidate++ + } else if val > candidate { + break + } + } + + if candidate > maxValue { + return 0, fmt.Errorf("Maximum number reached") + } + + return candidate, nil +}