Browse Source

Added list samba user api

aroz 1 year ago
parent
commit
5159520612
3 changed files with 174 additions and 110 deletions
  1. 56 0
      mod/fileservers/servers/samba/handlers.go
  2. 115 110
      mod/fileservers/servers/samba/smbuser.go
  3. 3 0
      network.go

+ 56 - 0
mod/fileservers/servers/samba/handlers.go

@@ -19,6 +19,7 @@ func (s *ShareManager) ListSambaShares(w http.ResponseWriter, r *http.Request) {
 	utils.SendJSONResponse(w, string(js))
 }
 
+// Add a samba share
 func (s *ShareManager) AddSambaShare(w http.ResponseWriter, r *http.Request) {
 	shareName, err := utils.GetPara(r, "name")
 	if err != nil {
@@ -53,6 +54,7 @@ func (s *ShareManager) AddSambaShare(w http.ResponseWriter, r *http.Request) {
 	utils.SendOK(w)
 }
 
+// Remove a samba share by name
 func (s *ShareManager) DelSambaShare(w http.ResponseWriter, r *http.Request) {
 	shareName, err := utils.GetPara(r, "name")
 	if err != nil {
@@ -88,3 +90,57 @@ func (s *ShareManager) DelSambaShare(w http.ResponseWriter, r *http.Request) {
 
 	utils.SendOK(w)
 }
+
+// Add a new samba user
+func (s *ShareManager) NewSambaUser(w http.ResponseWriter, r *http.Request) {
+	//TODO: Replace the GetPara to Post
+	username, err := utils.GetPara(r, "username")
+	if err != nil {
+		utils.SendErrorResponse(w, "username not given")
+		return
+	}
+
+	password, err := utils.GetPara(r, "password")
+	if err != nil {
+		utils.SendErrorResponse(w, "password not set")
+		return
+	}
+
+	err = s.AddSambaUser(username, password)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+// Remove a samba user
+func (s *ShareManager) DelSambaUser(w http.ResponseWriter, r *http.Request) {
+	username, err := utils.GetPara(r, "username")
+	if err != nil {
+		utils.SendErrorResponse(w, "username not given")
+		return
+	}
+
+	//Remove the samba user
+	err = s.RemoveSmbUser(username)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+// List all samba users info
+func (s *ShareManager) ListSambaUsers(w http.ResponseWriter, r *http.Request) {
+	userInfo, err := s.ListSambaUsersInfo()
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	js, _ := json.Marshal(userInfo)
+	utils.SendJSONResponse(w, string(js))
+}

+ 115 - 110
mod/fileservers/servers/samba/smbuser.go

@@ -3,149 +3,154 @@ package samba
 import (
 	"bufio"
 	"fmt"
-	"os"
+	"os/exec"
+	"regexp"
 	"strings"
 )
 
-// injectTdbsamToSmbConfig updates the smb.conf file to set the passdb backend to tdbsam if not already set
-func (m *ShareManager) injectTdbsamToSmbConfig() error {
-	// Path to smb.conf file
-	smbConfPath := m.SambaConfigPath
+// samba user info
+type UserInfo struct {
+	UnixUsername     string
+	UserSID          string
+	Domain           string
+	LastBadPassword  string
+	BadPasswordCount int
+}
 
-	// Open the smb.conf file for reading
-	file, err := os.OpenFile(smbConfPath, os.O_RDWR|os.O_APPEND, 0644)
+// AddSambaUser adds a Samba user with the given username
+func (s *ShareManager) AddSambaUser(username string, password string) error {
+	// Check if the user already exists
+	if !unixUserExists(username) {
+		//Create unix user
+		cmd := exec.Command("sudo", "adduser", "--no-create-home", "--disabled-password", "--disabled-login", "--force-badname", username)
+		err := cmd.Run()
+		if err != nil {
+			return fmt.Errorf("failed to add unix user: %v", err)
+		}
+	}
+
+	//Create samba user
+	err := s.setupSmbUser(username, password)
 	if err != nil {
-		return fmt.Errorf("failed to open smb.conf: %v", err)
+		return err
 	}
-	defer file.Close()
 
-	// Check if passdb backend is already set to tdbsam
-	passdbBackendSet := false
-	section := "[global]"
-	tdbsamLine := "passdb backend = tdbsam"
-	scanner := bufio.NewScanner(file)
-	for scanner.Scan() {
-		line := strings.TrimSpace(scanner.Text())
-		if line == section {
-			// Inside the [global] section, check for passdb backend setting
-			for scanner.Scan() {
-				subLine := strings.TrimSpace(scanner.Text())
-				if strings.HasPrefix(subLine, "passdb backend") {
-					// If passdb backend is already set to tdbsam, no need to update
-					if strings.TrimSpace(subLine) == tdbsamLine {
-						passdbBackendSet = true
-					}
-					break
-				}
-			}
-			break
-		}
+	return nil
+}
+
+// userExists checks if a user exists
+func unixUserExists(username string) bool {
+	// Check if the user exists by attempting to get their home directory
+	cmd := exec.Command("getent", "passwd", username)
+	err := cmd.Run()
+	return err == nil
+}
+
+// SetupSmbUser sets up a Samba user account with the given username and password
+func (s *ShareManager) setupSmbUser(username, password string) error {
+	// Execute the smbpasswd command with the specified username
+	cmd := exec.Command("sudo", "smbpasswd", "-a", username)
+
+	// Create a pipe for STDIN to pass the password to the smbpasswd command
+	stdinPipe, err := cmd.StdinPipe()
+	if err != nil {
+		return fmt.Errorf("failed to create STDIN pipe: %v", err)
 	}
-	if err := scanner.Err(); err != nil {
-		return fmt.Errorf("error reading smb.conf: %v", err)
+
+	// Start the command
+	if err := cmd.Start(); err != nil {
+		return fmt.Errorf("failed to start smbpasswd command: %v", err)
 	}
 
-	// If passdb backend is not set to tdbsam, append the line to the configuration file
-	if !passdbBackendSet {
-		if _, err := file.WriteString(fmt.Sprintf("%s\n", tdbsamLine)); err != nil {
-			return fmt.Errorf("failed to write to smb.conf: %v", err)
-		}
+	// Pass the password twice to the smbpasswd command via STDIN
+	password = fmt.Sprintf("%s\n%s\n", password, password)
+	if _, err := stdinPipe.Write([]byte(password)); err != nil {
+		return fmt.Errorf("failed to write password to smbpasswd command: %v", err)
+	}
+
+	// Close the STDIN pipe to signal the end of input
+	if err := stdinPipe.Close(); err != nil {
+		return fmt.Errorf("failed to close STDIN pipe: %v", err)
+	}
+
+	// Wait for the command to finish
+	if err := cmd.Wait(); err != nil {
+		return fmt.Errorf("smbpasswd command failed: %v", err)
 	}
 
 	return nil
 }
 
-// TdbsamInUse checks if the current smb runtime already using Tdbsam as backend
-func (m *ShareManager) TdbsamInUse() (bool, error) {
-	// Open the smb.conf file for reading
-	file, err := os.OpenFile(m.SambaConfigPath, os.O_RDWR|os.O_APPEND, 0644)
-	if err != nil {
-		return false, fmt.Errorf("failed to open smb.conf: %v", err)
-	}
-	defer file.Close()
+// RemoveSmbUser removes a Samba user account with the given username
+func (s *ShareManager) RemoveSmbUser(username string) error {
+	// Execute the smbpasswd command with the -x flag to delete the user
+	cmd := exec.Command("sudo", "smbpasswd", "-x", username)
 
-	// Check if passdb backend is already set to tdbsam
-	passdbBackendSet := false
-	section := "[global]"
-	tdbsamLine := "passdb backend = tdbsam"
-	scanner := bufio.NewScanner(file)
-	for scanner.Scan() {
-		line := strings.TrimSpace(scanner.Text())
-		if line == section {
-			// Inside the [global] section, check for passdb backend setting
-			for scanner.Scan() {
-				subLine := strings.TrimSpace(scanner.Text())
-				if strings.HasPrefix(subLine, "passdb backend") {
-					// If passdb backend is already set to tdbsam, no need to update
-					if strings.TrimSpace(subLine) == tdbsamLine {
-						passdbBackendSet = true
-					}
-					break
-				}
-			}
-			break
-		}
+	// Run the command
+	err := cmd.Run()
+	if err != nil {
+		return fmt.Errorf("failed to remove Samba user account: %v", err)
 	}
 
-	return passdbBackendSet, nil
+	return nil
 }
 
-// removeTdbsamFromSmbConfig removes the line 'passdb backend = tdbsam' from smb.conf
-func (m *ShareManager) removeTdbsamFromSmbConfig() error {
-	// Path to smb.conf file
-	smbConfPath := "/etc/samba/smb.conf"
+// RemoveUnixUser removes a Unix user account with the given username
+func (s *ShareManager) RemoveUnixUser(username string) error {
+	// Execute the userdel command with the specified username
+	cmd := exec.Command("sudo", "userdel", username)
 
-	// Open the smb.conf file for reading
-	file, err := os.OpenFile(smbConfPath, os.O_RDWR, 0644)
+	// Run the command
+	err := cmd.Run()
 	if err != nil {
-		return fmt.Errorf("failed to open smb.conf: %v", err)
+		return fmt.Errorf("failed to remove Unix user account: %v", err)
 	}
-	defer file.Close()
 
-	// Create a temporary file to store the modified content
-	tempFile, err := os.CreateTemp("", "smb.conf.temp")
+	return nil
+}
+
+// ListSambaUsersInfo lists information about Samba users
+func (s *ShareManager) ListSambaUsersInfo() ([]UserInfo, error) {
+	// Execute pdbedit -L -v command
+	cmd := exec.Command("sudo", "pdbedit", "-L", "-v")
+	output, err := cmd.CombinedOutput()
 	if err != nil {
-		return fmt.Errorf("failed to create temporary file: %v", err)
+		return nil, fmt.Errorf("failed to execute pdbedit command: %v", err)
 	}
-	defer os.Remove(tempFile.Name())
-	defer tempFile.Close()
 
-	// Copy lines from smb.conf to the temporary file, omitting the line 'passdb backend = tdbsam'
-	section := "[global]"
-	tdbsamLine := "passdb backend = tdbsam"
-	scanner := bufio.NewScanner(file)
+	// Parse the output and extract user information
+	var usersInfo []UserInfo
+	scanner := bufio.NewScanner(strings.NewReader(string(output)))
+	var userInfo UserInfo
+	re := regexp.MustCompile(`^Unix username:\s+(.*)$`)
 	for scanner.Scan() {
-		line := strings.TrimSpace(scanner.Text())
-		if line == section {
-			// Inside the [global] section, omit the line 'passdb backend = tdbsam'
-			for scanner.Scan() {
-				subLine := strings.TrimSpace(scanner.Text())
-				if strings.HasPrefix(subLine, "passdb backend") && subLine == tdbsamLine {
-					continue
-				}
-				tempFile.WriteString(fmt.Sprintf("%s\n", subLine))
+		line := scanner.Text()
+		match := re.FindStringSubmatch(line)
+		if len(match) > 0 {
+			if userInfo.UnixUsername != "" {
+				usersInfo = append(usersInfo, userInfo)
+			}
+			userInfo = UserInfo{UnixUsername: match[1]}
+		} else if strings.HasPrefix(line, "User SID:") {
+			userInfo.UserSID = strings.TrimSpace(strings.Split(line, ":")[1])
+		} else if strings.HasPrefix(line, "Domain:") {
+			userInfo.Domain = strings.TrimSpace(strings.Split(line, ":")[1])
+		} else if strings.HasPrefix(line, "Last bad password") {
+			info := strings.TrimSpace(strings.Split(line, ":")[1])
+			if info != "never" {
+				userInfo.LastBadPassword = info
 			}
+		} else if strings.HasPrefix(line, "Bad password count") {
+			fmt.Sscanf(strings.TrimSpace(strings.Split(line, ":")[1]), "%d", &userInfo.BadPasswordCount)
 		}
-		tempFile.WriteString(fmt.Sprintf("%s\n", line))
 	}
-	if err := scanner.Err(); err != nil {
-		return fmt.Errorf("error reading smb.conf: %v", err)
-	}
-
-	// Close the original smb.conf file
-	if err := file.Close(); err != nil {
-		return fmt.Errorf("error closing smb.conf: %v", err)
+	if userInfo.UnixUsername != "" {
+		usersInfo = append(usersInfo, userInfo)
 	}
 
-	// Remove the original smb.conf file
-	if err := os.Remove(smbConfPath); err != nil {
-		return fmt.Errorf("error removing smb.conf: %v", err)
-	}
-
-	// Rename the temporary file to smb.conf
-	if err := os.Rename(tempFile.Name(), smbConfPath); err != nil {
-		return fmt.Errorf("error renaming temporary file: %v", err)
+	if err := scanner.Err(); err != nil {
+		return nil, fmt.Errorf("error scanning pdbedit output: %v", err)
 	}
 
-	return nil
+	return usersInfo, nil
 }

+ 3 - 0
network.go

@@ -375,6 +375,9 @@ func FileServerInit() {
 	adminRouter.HandleFunc("/system/storage/samba/list", SambaShareManager.ListSambaShares)
 	adminRouter.HandleFunc("/system/storage/samba/add", SambaShareManager.AddSambaShare)
 	adminRouter.HandleFunc("/system/storage/samba/remove", SambaShareManager.DelSambaShare)
+	adminRouter.HandleFunc("/system/storage/samba/addUser", SambaShareManager.NewSambaUser)
+	adminRouter.HandleFunc("/system/storage/samba/delUser", SambaShareManager.DelSambaUser)
+	adminRouter.HandleFunc("/system/storage/samba/listUsers", SambaShareManager.ListSambaUsers)
 
 	networkFileServerDaemon = append(networkFileServerDaemon, &fileservers.Server{
 		ID:                "webdav",