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
+}