Skip to content
Snippets Groups Projects
Commit 597d9edb authored by Theo's avatar Theo :troll:
Browse files

improve validation and refactor opts

parent 90a2728d
Branches
No related tags found
No related merge requests found
......@@ -110,7 +110,7 @@ func bulkCreate(cmd *cobra.Command, args []string) error {
err := validate.GRR(users, u.GRR)
if err != nil {
fmt.Printf("Entry %v: malformed GRR: %v\nSkipping...\n", i+1, u.GRR)
fmt.Printf("Entry %v: malformed GRR: %v\nSkipping...\n", i+1, err)
continue
}
......
......@@ -19,8 +19,6 @@ var CreateCmd = &cobra.Command{
}
func init() {
// possible flags
// FIXME: maybe leave less flags for user input
CreateCmd.Flags().StringP("grr", "r", "_", "User GRR, required for ini type")
CreateCmd.Flags().StringP("type", "t", "ini", "Type of auto-generated login: ini, first or last")
CreateCmd.Flags().StringP("name", "n", "_", "User full name, required, use quotes for spaces")
......@@ -35,7 +33,6 @@ func init() {
CreateCmd.Flags().StringP("nobkp", "b", "", "User nobackup directory path")
CreateCmd.Flags().BoolP("web", "w", false, "Generate webdir")
// required flags
CreateCmd.MarkFlagRequired("name")
CreateCmd.MarkFlagRequired("group")
......@@ -79,7 +76,7 @@ func createUserCmd(cmd *cobra.Command, args []string) error {
err = utils.ClearCache()
if err != nil {
fmt.Printf("Failed to reload cache, changes might take a while\nError: %v\n", err)
fmt.Printf("Failed to reload cache, changes might take a while to take effect\nError: %v\n", err)
}
return nil
......@@ -95,10 +92,6 @@ func createNewUserModel(cmd *cobra.Command) (user.User, bool, error) {
return u, false, err
}
if err := validateInputs(opts); err != nil {
return u, false, err
}
if opts.Status == "Blocked" {
opts.Shell = "/bin/false"
}
......@@ -129,11 +122,14 @@ func createNewUserModel(cmd *cobra.Command) (user.User, bool, error) {
return u, false, err
}
return u, opts.Confirm, nil
if err := validateUser(u); err != nil {
return u, false, err
}
func validateInputs(opts opts.Opts) error {
return u, opts.Confirm, nil
}
func validateUser(u user.User) error {
l, err := auth.ConnLDAP()
if err != nil {
return err
......@@ -150,33 +146,13 @@ func validateInputs(opts opts.Opts) error {
return err
}
err = validate.GID(groups, opts.GID)
if err != nil {
return err
}
err = validate.GRR(users, opts.GRR)
if err != nil {
return err
}
err = validate.Expiry(opts.Expiry)
if err != nil {
return err
}
err = validate.Status(opts.Status)
if err != nil {
return err
}
// it’s OK if UID is empty here, we generate it later :)
if opts.UID != "" {
err := validate.UID(users, opts.UID)
if err != nil {
return err
}
}
return nil
return validate.All(
func() error { return validate.Status(u.Status) },
func() error { return validate.Expiry(u.Expiry) },
func() error { return validate.GRR(users, u.GRR) },
func() error { return validate.LoginUnique(users, u.UID) },
func() error { return validate.GroupUnique(groups, u.GID) },
func() error { return validate.PathExists(u.Homedir, "homedir") },
func() error { return validate.PathExists(u.Nobackup, "nobackup") },
)
}
......@@ -12,7 +12,7 @@ import (
var GroupCmd = &cobra.Command{
Use: "group [username] [new-group]",
Short: "Change a user's base group",
Example: " useradm user group temp2 prof",
Example: " useradm user group mvrp22 ppginf",
Args: cobra.ExactArgs(2),
RunE: groupUserCmd,
}
......@@ -44,6 +44,7 @@ func groupUserCmd(cmd *cobra.Command, args []string) error {
return err
}
// checks if group does in fact exist
_, err = utils.GetGIDNumFromGID(groups, args[1])
if err != nil {
return err
......
package user
// FIXME: mudanças não são mostradas kkkkkk
import (
"fmt"
"os"
......@@ -82,13 +81,13 @@ func modUserCmd(cmd *cobra.Command, args []string) error {
changes, err := promptUserYaml(state)
if changes.GRR != curr.GRR {
err = validate.GRR(users, changes.GRR)
err := validate.GRR(users, changes.GRR)
if err != nil {
return err
}
}
err = validate.GID(groups, changes.Group)
err = validate.GroupUnique(groups, changes.Group)
if err != nil {
return err
}
......
......@@ -9,6 +9,7 @@ import (
"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/internal/validate"
"gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils"
)
......@@ -59,17 +60,28 @@ func removeUserCmd(cmd *cobra.Command, args []string) error {
utils.ConfirmationPrompt(opts.Confirm, "user removal")
err = validate.PathExists(filepath.Join(manage.HOME_TRASH,
filepath.Base(u.Homedir)), "homedir")
if err != nil {
return err
}
err = validate.PathExists(filepath.Join(manage.NO_BKP_TRASH,
filepath.Base(u.Nobackup)), "nobackup")
if err != nil {
return err
}
if u.Webdir != "_" {
err = validate.PathExists(filepath.Join(manage.WEB_TRASH,
filepath.Base(u.Webdir)), "webdir")
if err != nil {
return err
}
}
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
// and the defer will activate. That moves the trash dir
// to the users home which is bad, so if a dir move fails,
// it's better not to try to fix it. Just warn the admin
// to do it by hand, either removing the destination dir
// or changing the name. Remeber that having no dir to move
// is not a fail, so you can run the command again no fear
success = true
return err
}
......
package user
// Pretty bad tbh
import (
"fmt"
"os"
......@@ -66,7 +67,7 @@ func tempCreate(cmd *cobra.Command, args []string) error {
}
}
err = validate.GID(groups, opts.GID)
err = validate.GroupUnique(groups, opts.GID)
if err != nil {
return err
}
......
......@@ -35,8 +35,7 @@ func unblockUserCmd(cmd *cobra.Command, args []string) error {
}
defer l.Close()
login := args[0]
u, err := manage.Locate(l, login)
u, err := manage.Locate(l, args[0])
if err != nil {
return err
}
......
......@@ -48,7 +48,7 @@ func ModKRBPassword(login, password string) error {
}
if err != nil {
return fmt.Errorf("Command execution failed: %v\nOutput:\n%s", err, outStr)
return fmt.Errorf("Password change failed: %v\nOutput:\n%s", err, outStr)
}
return nil
......@@ -59,7 +59,7 @@ func DelKRBPrincipal(login string) error {
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Fail to delete Kerberos principal: %v\nOutput: %s", err, output)
return fmt.Errorf("Failed to delete Kerberos principal: %v\nOutput: %s", err, output)
}
return nil
......@@ -70,7 +70,7 @@ func KRBRandKey(login string) error {
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("Fail to set randkey in Kerberos: %v\nOutput: %s", err, output)
return fmt.Errorf("Failed to set randkey in Kerberos: %v\nOutput: %s", err, output)
}
return nil
......
......@@ -14,10 +14,13 @@ import (
const (
PASSWD_PATH = "/etc/ldapscripts/ldapscripts.passwd"
STD_DC = "dc=c3local"
GROUP_REQ = "ou=grupos," + STD_DC
MIN_GID = 1000
MAX_GID = 5000
MIN_UID = 1000
MAX_UID = 30000
SEARCH_FAIL = "LDAP search failed: %w"
)
// stablishes a connection to LDAP
......@@ -86,10 +89,10 @@ func AddUserToLDAP(l *ldap.Conn, u *user.User) error {
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",
GROUP_REQ,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
"(memberUid="+UID+")", // Filter by UID membership
"(memberUid="+UID+")", // filter by UID membership
[]string{"dn", "cn"},
nil,
)
......@@ -119,17 +122,14 @@ func DelFromLDAP(l *ldap.Conn, UID string) error {
}
func CreateGroupLDAP(l *ldap.Conn, GID, GIDNumber string) error {
// contruct dn
dn := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID)
dn := fmt.Sprintf("cn=%s,%s", GID, GROUP_REQ)
req := ldap.NewAddRequest(dn, []ldap.Control{})
// contruct request
req.Attribute("objectClass", []string{"posixGroup"})
req.Attribute("cn", []string{GID})
req.Attribute("gidNumber", []string{GIDNumber})
// create
if err := l.Add(req); err != nil {
return fmt.Errorf("failed to add group: %w", err)
}
......@@ -138,12 +138,11 @@ func CreateGroupLDAP(l *ldap.Conn, GID, GIDNumber string) error {
}
func DeleteGroupLDAP(l *ldap.Conn, GID string) error {
// construct dn
dn := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID)
dn := fmt.Sprintf("cn=%s,%s", GID, GROUP_REQ)
delReq := ldap.NewDelRequest(dn, []ldap.Control{})
req := ldap.NewDelRequest(dn, []ldap.Control{})
if err := l.Del(delReq); err != nil {
if err := l.Del(req); err != nil {
return fmt.Errorf("failed to delete group: %w", err)
}
......@@ -151,26 +150,26 @@ func DeleteGroupLDAP(l *ldap.Conn, 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.Add("memberUid", []string{UID})
groupDN := fmt.Sprintf("cn=%s,%s", GID, GROUP_REQ)
req := ldap.NewModifyRequest(groupDN, nil)
req.Add("memberUid", []string{UID})
err := l.Modify(modReq)
err := l.Modify(req)
if err != nil {
return fmt.Errorf("Failed to add user %s to group %s: %v", UID, GID, err)
return fmt.Errorf("failed to add user %s to group %s: %w", UID, GID, err)
}
return nil
}
func DelFromGroupLDAP(l *ldap.Conn, userUID, GID string) error {
groupDN := fmt.Sprintf("cn=%s,ou=grupos,dc=c3local", GID)
groupDN := fmt.Sprintf("cn=%s,%s", GID, GROUP_REQ)
modReq := ldap.NewModifyRequest(groupDN, nil)
modReq.Delete("memberUid", []string{userUID})
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)
return fmt.Errorf("failed to remove user %s from group %s: %w", userUID, groupDN, err)
}
return nil
......@@ -191,7 +190,7 @@ func GetAllUsersLDAP(l *ldap.Conn) (*ldap.SearchResult, error) {
// create the LDAP search request
req := ldap.NewSearchRequest(
"dc=c3local", // search base
STD_DC, // search base
ldap.ScopeWholeSubtree, // scope
ldap.NeverDerefAliases, // aliases
0, 0, // size/time limit
......@@ -205,7 +204,7 @@ func GetAllUsersLDAP(l *ldap.Conn) (*ldap.SearchResult, error) {
// perform the search
sr, err := l.Search(req)
if err != nil {
err = fmt.Errorf("Failed to fetch users from LDAP: %v", err)
err = fmt.Errorf("failed to fetch users from LDAP: %w", err)
return sr, err
}
......@@ -217,7 +216,7 @@ func GetAllGroupsLDAP(l *ldap.Conn) (map[string]string, error) {
// build search
req := ldap.NewSearchRequest(
"dc=c3local",
STD_DC,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0, 0, false,
......@@ -229,7 +228,7 @@ func GetAllGroupsLDAP(l *ldap.Conn) (map[string]string, error) {
// search
sr, err := l.Search(req)
if err != nil {
return nil, fmt.Errorf("LDAP search failed: %w", err)
return nil, fmt.Errorf(SEARCH_FAIL, err)
}
// arrange result into a map string->string
......@@ -249,7 +248,7 @@ func GetAllUIDsLDAP(l *ldap.Conn) ([]int, error) {
// build search
req := ldap.NewSearchRequest(
"dc=c3local",
STD_DC,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0, 0, false,
......@@ -261,7 +260,7 @@ func GetAllUIDsLDAP(l *ldap.Conn) ([]int, error) {
// search
sr, err := l.Search(req)
if err != nil {
return nil, fmt.Errorf("LDAP search failed: %w", err)
return nil, fmt.Errorf(SEARCH_FAIL, err)
}
// arrange result into a array of integers
......@@ -291,7 +290,7 @@ func GetNewUIDNumLDAP(l *ldap.Conn) (string, error) {
res, err := utils.GetMEX(uids, MIN_UID, MAX_UID)
if err != nil {
return "", fmt.Errorf("Error generating new uidNumber: %w", err)
return "", fmt.Errorf("error generating new uidNumber: %w", err)
}
return strconv.Itoa(res), nil
......@@ -314,7 +313,7 @@ func GetAllGIDsLDAP(l *ldap.Conn) ([]int, error) {
// search
sr, err := l.Search(req)
if err != nil {
return nil, fmt.Errorf("LDAP search failed: %w", err)
return nil, fmt.Errorf(SEARCH_FAIL, err)
}
// arrange result into a array of integers
......@@ -344,7 +343,7 @@ func GetNewGIDNumLDAP(l *ldap.Conn) (string, error) {
res, err := utils.GetMEX(gids, MIN_GID, MAX_GID)
if err != nil {
return "", fmt.Errorf("Error generating new gidNumber: %w", err)
return "", fmt.Errorf("error generating new gidNumber: %w", err)
}
return strconv.Itoa(res), nil
......
......@@ -57,112 +57,67 @@ func (o *Opts) ToString() string {
}
func (o *Opts) RetrieveOpts(cmd *cobra.Command) error {
var err error
o.GRR, err = getFlagString(cmd, "grr")
if err != nil {
return err
strFlags := map[string]*string{
"grr": &o.GRR,
"path": &o.Path,
"resp": &o.Resp,
"name": &o.Name,
"type": &o.Ltype,
"group": &o.GID,
"login": &o.UID,
"shell": &o.Shell,
"nobkp": &o.Nobkp,
"status": &o.Status,
"expiry": &o.Expiry,
"passwd": &o.Passwd,
"homedir": &o.Homedir,
}
o.Resp, err = getFlagString(cmd, "resp")
if err != nil {
return err
boolFlags := map[string]*bool{
"web": &o.Webdir,
"block": &o.Block,
"exact": &o.Exact,
"ignore": &o.Ignore,
"confirm": &o.Confirm,
"unblock": &o.Unblock,
}
o.Name, err = getFlagString(cmd, "name")
if err != nil {
return err
intFlags := map[string]*int{
"number": &o.Number,
}
o.GID, err = getFlagString(cmd, "group")
for name, ptr := range strFlags {
val, err := getFlagString(cmd, name)
if err != nil {
return err
return fmt.Errorf("%s: %w", name, err)
}
o.UID, err = getFlagString(cmd, "login")
if err != nil {
return err
*ptr = val
}
o.Ltype, err = getFlagString(cmd, "type")
for name, ptr := range boolFlags {
val, err := getFlagBool(cmd, name)
if err != nil {
return err
return fmt.Errorf("%s: %w", name, err)
}
o.Shell, err = getFlagString(cmd, "shell")
if err != nil {
return err
*ptr = val
}
o.Webdir, err = getFlagBool(cmd, "web")
for name, ptr := range intFlags {
val, err := getFlagInt(cmd, name)
if err != nil {
return err
return fmt.Errorf("%s: %w", name, err)
}
o.Path, err = getFlagString(cmd, "path")
if err != nil {
return err
}
o.Nobkp, err = getFlagString(cmd, "nobkp")
if err != nil {
return err
}
o.Status, err = getFlagString(cmd, "status")
if err != nil {
return err
}
o.Expiry, err = getFlagString(cmd, "expiry")
if err != nil {
return err
}
o.Passwd, err = getFlagString(cmd, "passwd")
if err != nil {
return err
}
o.Homedir, err = getFlagString(cmd, "homedir")
if err != nil {
return err
}
o.Confirm, err = getFlagBool(cmd, "confirm")
if err != nil {
return err
}
o.Unblock, err = getFlagBool(cmd, "unblock")
if err != nil {
return err
}
o.Block, err = getFlagBool(cmd, "block")
if err != nil {
return err
}
o.Ignore, err = getFlagBool(cmd, "ignore")
if err != nil {
return err
}
o.Exact, err = getFlagBool(cmd, "exact")
if err != nil {
return err
}
o.Number, err = getFlagInt(cmd, "number")
if err != nil {
return err
*ptr = val
}
return nil
}
// since this model is used in most subcommands, some
const (
FLAG_RET_FAIL = "failed to get flag %q: %w"
)
// since this entity is used in most subcommands, some
// flags may exist in one command but not in another,
// so if it doesn't exist just set as empty before
// the error occurs, it won't be used anyway... :D
......@@ -174,7 +129,7 @@ func getFlagString(cmd *cobra.Command, flag string) (string, error) {
flagValue, err := cmd.Flags().GetString(flag)
if err != nil {
return "", fmt.Errorf("failed to get flag %q: %w", flag, err)
return "", fmt.Errorf(FLAG_RET_FAIL, flag, err)
}
return flagValue, nil
}
......@@ -186,7 +141,7 @@ func getFlagInt(cmd *cobra.Command, flag string) (int, error) {
flagValue, err := cmd.Flags().GetInt(flag)
if err != nil {
return 0, fmt.Errorf("failed to get flag %q: %w", flag, err)
return 0, fmt.Errorf(FLAG_RET_FAIL, flag, err)
}
return flagValue, nil
}
......@@ -198,7 +153,7 @@ func getFlagBool(cmd *cobra.Command, flag string) (bool, error) {
flagValue, err := cmd.Flags().GetBool(flag)
if err != nil {
return false, fmt.Errorf("failed to get flag %q: %w", flag, err)
return false, fmt.Errorf(FLAG_RET_FAIL, flag, err)
}
return flagValue, nil
}
......@@ -18,7 +18,7 @@ import (
const (
STD_PERM = "0700" // default user dir permission
BLK_PERM = "0000" // user dir permission if blocked
MAX_VARIANCE = 60 // stops iteration at some point
MAX_VARIANCE = 60 // attempts at login generation
HTML_BASE_PERM = "0755" // set public_html to this on creation
NUM_GECOS_FIELDS = 8
)
......@@ -117,20 +117,22 @@ search made: "useradm user show -l %v -e"`, login)
return &u, nil
}
const FAILED_PERMISSION = "failed to set permissions: %w"
func Block(u *user.User) error {
err := auth.KRBRandKey(u.UID) // new password, no one will know
if err != nil {
return err
}
cmd := exec.Command("chmod", "0000", u.Homedir)
cmd := exec.Command("chmod", BLK_PERM, u.Homedir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to set permissions: %w", err)
return fmt.Errorf(FAILED_PERMISSION, err)
}
cmd = exec.Command("chmod", "0000", u.Nobackup)
cmd = exec.Command("chmod", BLK_PERM, u.Nobackup)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to set permissions: %w", err)
return fmt.Errorf(FAILED_PERMISSION, err)
}
l, err := auth.ConnLDAP()
......@@ -151,14 +153,14 @@ func Block(u *user.User) error {
}
func Unblock(u *user.User, pass string) error {
cmd := exec.Command("chmod", "0700", u.Homedir)
cmd := exec.Command("chmod", STD_PERM, u.Homedir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to set permissions: %w", err)
return fmt.Errorf(FAILED_PERMISSION, err)
}
cmd = exec.Command("chmod", "0700", u.Nobackup)
cmd = exec.Command("chmod", STD_PERM, u.Nobackup)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to set permissions: %w", err)
return fmt.Errorf(FAILED_PERMISSION, err)
}
l, err := auth.ConnLDAP()
......@@ -171,7 +173,7 @@ func Unblock(u *user.User, pass string) error {
mod.Replace("loginShell", []string{"/bin/bash"})
if err := l.Modify(mod); err != nil {
return fmt.Errorf("Failed to update loginShell for %s: %w", u.UID, err)
return fmt.Errorf("failed to update loginShell for %s: %w", u.UID, err)
}
if pass == "_" {
......@@ -310,25 +312,25 @@ func CreateHome(u *user.User, homeDir string) error {
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to create home directory: %w", err)
return fmt.Errorf("failed to create home directory: %w", err)
}
// copy /etc/skel
cmd = exec.Command("cp", "-r", "/etc/skel/.", homeDir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to copy /etc/skel contents: %w", err)
return fmt.Errorf("failed to copy /etc/skel contents: %w", err)
}
// change permissions
cmd = exec.Command("chmod", STD_PERM, homeDir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to set permissions: %w", err)
return fmt.Errorf("failed to set permissions: %w", err)
}
// change ownership
cmd = exec.Command("chown", "-R", fmt.Sprintf("%s:%s", u.UID, u.GID), homeDir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to change ownership: %w", err)
return fmt.Errorf("failed to change ownership: %w", err)
}
return nil
......@@ -353,37 +355,37 @@ func CreateWeb(u *user.User) error {
cmd.Stdout = nil
cmd.Stderr = nil
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to create web directory: %w", err)
return fmt.Errorf("failed to create web directory: %w", err)
}
// create index
cmd = exec.Command("touch", filepath.Join(u.Webdir, "index.html"))
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to create index.html: %w", err)
return fmt.Errorf("failed to create index.html: %w", err)
}
// create link in users home
cmd = exec.Command("ln", "-s", u.Webdir, filepath.Join(u.Homedir, "public_html"))
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to create link public_html: %w", err)
return fmt.Errorf("failed to create link public_html: %w", err)
}
// change permissions
cmd = exec.Command("chmod", STD_PERM, u.Webdir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to set permissions: %w", err)
return fmt.Errorf("failed to set permissions: %w", err)
}
// change ownership
cmd = exec.Command("chown", "-R", fmt.Sprintf("%s:%s", u.UID, u.GID), u.Webdir)
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to change ownership: %w", err)
return fmt.Errorf("failed to change ownership: %w", err)
}
// set permission for public_html
cmd = exec.Command("chmod", HTML_BASE_PERM, filepath.Join(u.Homedir, "public_html"))
if err := cmd.Run(); err != nil {
return fmt.Errorf("Failed to create set permissions for public_html: %w", err)
return fmt.Errorf("failed to create set permissions for public_html: %w", err)
}
success = true
......@@ -460,7 +462,7 @@ func GenMissingFields(u *user.User) error {
if u.UIDNumber == "" {
uidNum, err := auth.GetNewUIDNumLDAP(l)
if err != nil {
return fmt.Errorf("Failed to generate new UIDNumber for user: %v", err)
return fmt.Errorf("failed to generate new UIDNumber for user: %v", err)
}
u.UIDNumber = uidNum
}
......
......@@ -2,6 +2,7 @@ package validate
import (
"fmt"
"maps"
"regexp"
"strings"
......@@ -10,65 +11,247 @@ import (
"gitlab.c3sl.ufpr.br/tss24/useradm/pkg/utils"
)
const (
AlreadyExistsLDAP = "already exists in LDAP"
CantBeEmpty = "cannot be empty"
)
type ValidationError struct {
Field string
Value interface{}
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %v - %s", e.Field, e.Value, e.Message)
}
type MultiValidationError []ValidationError
func (e MultiValidationError) Error() string {
var sb strings.Builder
sb.WriteString("Validation failed:\n")
for _, err := range e {
sb.WriteString(fmt.Sprintf("- %s\n", err.Error()))
}
return sb.String()
}
func Expiry(expiry string) error {
if expiry == "" {
return ValidationError{
Field: "expiry",
Value: expiry,
Message: CantBeEmpty,
}
}
if expiry == "_" {
return nil
}
parts := strings.Split(expiry, ".")
if !utils.IsValidDate(parts) {
err := fmt.Errorf("Malformed expiry date string, use \"dd.mm.yy\"")
return err
if !utils.IsValidDate(strings.Split(expiry, ".")) {
return ValidationError{
Field: "expiry",
Value: expiry,
Message: "must be in dd.mm.yy format",
}
}
return nil
}
// TODO: change this check
func Status(status string) error {
if status != "Blocked" && status != "Active" {
return fmt.Errorf("User status can only be \"Active\" or \"Blocked\"")
ok := map[string]bool{"Active": true, "Blocked": true}
if status == "" {
return ValidationError{
Field: "status",
Value: status,
Message: CantBeEmpty,
}
}
if !ok[status] {
return ValidationError{
Field: "status",
Value: status,
Message: fmt.Sprintf("must be one of: %v", maps.Keys(ok)),
}
}
return nil
}
func GRR(users []user.User, grr string) error {
// OK if empty, only "ini" login type requires it and we check :)
if grr == "" {
return ValidationError{
Field: "grr",
Value: grr,
Message: CantBeEmpty,
}
}
if grr == "_" {
return nil
}
isValid, _ := regexp.MatchString(`^\d{8}$`, grr) // is 8 digit number
if !isValid {
err := fmt.Errorf("Malformed GRR string, must be 8 digit number")
return err
if match, _ := regexp.MatchString(`^\d{8}$`, grr); !match {
return ValidationError{
Field: "grr",
Value: grr,
Message: "must be an 8-digit number",
}
}
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 ValidationError{
Field: "grr",
Value: grr,
Message: AlreadyExistsLDAP,
}
}
return nil
}
func GroupUnique(groups map[string]string, group string) error {
if group == "" {
return ValidationError{
Field: "group",
Value: group,
Message: CantBeEmpty,
}
}
for _, v := range groups {
if v == group {
return ValidationError{
Field: "group",
Value: group,
Message: AlreadyExistsLDAP,
}
}
}
return nil
}
func Login(login string) error {
if login == "" {
return ValidationError{
Field: "login",
Value: login,
Message: CantBeEmpty,
}
}
return nil
}
func LoginUnique(users []user.User, login string) error {
if err := Login(login); err != nil {
return err
}
if res := manage.Search(users, false, true, login, "", "", "", "", ""); len(res) > 0 {
return ValidationError{
Field: "login",
Value: login,
Message: AlreadyExistsLDAP,
}
}
return nil
}
func GID(groups map[string]string, group string) error {
var err error
func UserName(name string) error {
if name == "" {
return ValidationError{
Field: "name",
Value: name,
Message: CantBeEmpty,
}
}
for _, value := range groups {
if value == group {
if name == "_" {
return ValidationError{
Field: "name",
Value: name,
Message: "reserved placeholder value",
}
}
return nil
}
func PathExists(path, field string) error {
if path == "" || path == "_" {
return ValidationError{
Field: field,
Value: path,
Message: CantBeEmpty,
}
}
if utils.PathExists(path) {
return ValidationError{
Field: field,
Value: path,
Message: fmt.Sprintf("path %v already exists", path),
}
}
err = fmt.Errorf("Could't find group \"%v\" in LDAP database", group)
return nil
}
func PathDoesntExist(path, field string) error {
if path == "" || path == "_" {
return ValidationError{
Field: field,
Value: path,
Message: CantBeEmpty,
}
}
if !utils.PathExists(path) {
return ValidationError{
Field: field,
Value: path,
Message: fmt.Sprintf("path %v doesn't exist", path),
}
}
return nil
}
func UserNameUnique(users []user.User, name string) error {
if err := UserName(name); err != nil {
return err
}
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)
if res := manage.Search(users, true, true, "", "", name, "", "", ""); len(res) > 0 {
return ValidationError{
Field: "name",
Value: name,
Message: AlreadyExistsLDAP,
}
}
return nil
}
func All(validators ...func() error) error {
var errs MultiValidationError
for _, validator := range validators {
if err := validator(); err != nil {
if verr, ok := err.(ValidationError); ok {
errs = append(errs, verr)
} else {
return fmt.Errorf("unexpected error type: %w", err)
}
}
}
if len(errs) > 0 {
return errs
}
return nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment