Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • refactor/folder-structure
2 results

remove.go

Blame
  • remove.go 4.72 KiB
    package user
    
    import (
    	"fmt"
    	"log"
    	"os"
    	"os/exec"
    	"path/filepath"
    	"strconv"
    	"time"
    
    	"github.com/go-ldap/ldap/v3"
    	"github.com/spf13/cobra"
    	"gitlab.c3sl.ufpr.br/tss24/useradm/model"
    )
    
    var (
    	ANO          = strconv.Itoa(time.Now().Year())
    	NO_BKP_TRASH = "/nobackup/contas_removidas/" + ANO
    	HOME_TRASH   = "/home/contas_removidas/" + ANO
    	WEB_TRASH    = "/home/contas_removidas/html/" + ANO
    )
    
    var RemoveUserCmd = &cobra.Command{
    	Use:   "remove [username]",
    	Short: "Delete a user",
        Args:  cobra.ExactArgs(1),
    	RunE:  removeUserFunc,
    }
    
    func init() {
    	RemoveUserCmd.Flags().BoolP("confirm", "y", false, "Skip confirmation prompt")
    }
    
    func removeUserFunc(cmd *cobra.Command, args []string) error {
    	var opts model.Opts
    	success := false
    
    	err := opts.RetrieveOpts(cmd)
    	if err != nil {
    		return err
    	}
    
        login := args[0]
    	u, err := locateUser(login)
    	if err != nil {
    		return err
    	}
    
    	defer func() {
    		if !success {
    			log.Println("Found error, rolling back dirs...")
    			_ = moveAndChown(filepath.Join(NO_BKP_TRASH, filepath.Base(u.Nobackup)),
    				u.Nobackup, u.UID, u.GID)
    			_ = moveAndChown(filepath.Join(HOME_TRASH, filepath.Base(u.Homedir)),
    				u.Homedir, u.UID, u.GID)
    			_ = moveAndChown(filepath.Join(WEB_TRASH, filepath.Base(u.Webdir)),
    				u.Webdir, u.UID, u.GID)
    		}
    	}()
    
    	fmt.Printf("Found %v\n\n", u.ToString())
    
    	confirmationPrompt(opts.Confirm, "removal")
    
    	err = removeDirs(u)
    	if err != nil {
    		return err
    	}
    
    	err = delUserLDAP(u)
    	if err != nil {
    		return err
    	}
    
    	fmt.Printf("\nUser removed!\n")
    
    	success = true
    	return nil
    }
    
    // searches for the specified login string, there can only be one >:)
    func locateUser(login string) (model.User, error) {
    	var u model.User
    	users, err := getUsers()
    
    	if !loginExists(users, login) {
    		return u, fmt.Errorf("Failed to find login in LDAP database: %v", err)
    	}
    
    	filter := searchUser(users, false, login, "", "", "", "", "")
    	if len(filter) != 1 {
    		return u, fmt.Errorf(`More than one user matched the login given.
    search made: "useradm user show -l %v"`, login)
    	}
    
    	u = filter[0]
    	return u, nil
    }
    
    // moves dirs to their respective trash dir
    func removeDirs(u model.User) error {
    	err := moveAndChown(u.Homedir, HOME_TRASH, "nobody", "nogroup")
    	if err != nil {
    		return err
    	}
    
    	err = moveAndChown(u.Nobackup, NO_BKP_TRASH, "nobody", "nogroup")
    	if err != nil {
    		return err
    	}
    
    	err = moveAndChown(u.Webdir, WEB_TRASH, "nobody", "nogroup")
    	if err != nil {
    		return err
    	}
    
    	return nil
    }
    
    func delUserLDAP(u model.User) error {
    	l, err := connLDAP()
    	if err != nil {
    		return err
    	}
    	defer l.Close()
    
    	// first remove user from all groups
    	searchReq := ldap.NewSearchRequest(
    		"ou=grupos,dc=c3local,dc=com",
    		ldap.ScopeWholeSubtree,
    		ldap.NeverDerefAliases, 0, 0, false,
    		"(memberUid="+u.UID+")", // filter by UID membership
    		[]string{"dn"},
    		nil,
    	)
    
    	// searches groups for target
    	groups, err := l.Search(searchReq)
    	if err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
    		return fmt.Errorf("Group members search failed: %v", err)
    	}
    
    	// removing from groups
    	for _, entry := range groups.Entries {
    		modReq := ldap.NewModifyRequest(entry.DN, nil)
    		modReq.Delete("memberUid", []string{u.UID})
    		if err := l.Modify(modReq); err != nil &&
    			!ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchAttribute) {
    			log.Printf("Warning: Failed to remove from group %s: %v", entry.DN, err)
    		}
    	}
    
    	// removing user entry
    	delReq := ldap.NewDelRequest(u.DN, nil)
    	if err := l.Del(delReq); err != nil &&
    		!ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
    		return fmt.Errorf("user deletion failed: %v", err)
    	}
    
    	return nil
    }
    
    // usually not necessary but used in a failed create command
    func delKerberosPrincipal(login string) error {
    	cmd := exec.Command("kadmin.local", "-q", fmt.Sprintf("delprinc -force %s", login))
    
    	output, err := cmd.CombinedOutput()
    	if err != nil {
    		return fmt.Errorf("Fail to delete Kerberos principal: %v\nOutput: %s", err, output)
    	}
    
    	return nil
    }
    
    func moveAndChown(orig, dest, owner, group string) error {
    	// check if orig exists
    	if _, err := os.Stat(orig); err != nil {
    		log.Printf("Directory %v not found so not moved\n", orig)
    		return nil
    	}
    
    	// construct destination path
    	destPath := filepath.Join(dest, filepath.Base(orig))
    
    	if _, err := os.Stat(destPath); err == nil {
    		return fmt.Errorf("Directory %v already exists, can't move\n", destPath)
    	}
    
    	// move directory
    	cmd := exec.Command("mv", orig, destPath)
    	if output, err := cmd.CombinedOutput(); err != nil {
    		return fmt.Errorf("Failed to move dirs: %w\nOutput: %v", err, output)
    	}
    
    	// recursive chown
    	cmd = exec.Command("chown", "-R", owner+":"+group, destPath)
    	if output, err := cmd.CombinedOutput(); err != nil {
    		return fmt.Errorf("Failed to set owner/group: %w\nOutput: %v", err, output)
    	}
    
    	return nil
    }