Browse Source

Added more samba api

aroz 1 năm trước cách đây
mục cha
commit
2194b9e049

+ 7 - 5
auth.go

@@ -151,7 +151,7 @@ func AuthSettingsInit() {
 // Validate secure request that use authreq.html
 // Require POST: password and admin permission
 // return true if authentication passed
-func AuthValidateSecureRequest(w http.ResponseWriter, r *http.Request) bool {
+func AuthValidateSecureRequest(w http.ResponseWriter, r *http.Request, requireAdmin bool) bool {
 	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
 	if err != nil {
 		w.WriteHeader(http.StatusUnauthorized)
@@ -159,10 +159,12 @@ func AuthValidateSecureRequest(w http.ResponseWriter, r *http.Request) bool {
 		return false
 	}
 
-	if !userinfo.IsAdmin() {
-		w.WriteHeader(http.StatusForbidden)
-		w.Write([]byte("403 Forbidden"))
-		return false
+	if requireAdmin {
+		if !userinfo.IsAdmin() {
+			w.WriteHeader(http.StatusForbidden)
+			w.Write([]byte("403 Forbidden"))
+			return false
+		}
 	}
 
 	//Double check password for this user

+ 2 - 2
disk.go

@@ -160,13 +160,13 @@ func DiskServiceInit() {
 				adminRouter.HandleFunc("/system/disk/raid/list", raidManager.HandleListRaidDevices)
 				adminRouter.HandleFunc("/system/disk/raid/new", raidManager.HandleCreateRAIDDevice)
 				adminRouter.HandleFunc("/system/disk/raid/remove", func(w http.ResponseWriter, r *http.Request) {
-					if !AuthValidateSecureRequest(w, r) {
+					if !AuthValidateSecureRequest(w, r, true) {
 						return
 					}
 					raidManager.HandleRemoveRaideDevice(w, r)
 				})
 				adminRouter.HandleFunc("/system/disk/raid/assemble", func(w http.ResponseWriter, r *http.Request) {
-					if !AuthValidateSecureRequest(w, r) {
+					if !AuthValidateSecureRequest(w, r, true) {
 						return
 					}
 					raidManager.HandleForceAssembleReload(w, r)

+ 2 - 2
hardware.power.go

@@ -39,7 +39,7 @@ func hardware_power_checkIfHardware(w http.ResponseWriter, r *http.Request) {
 
 func hardware_power_poweroff(w http.ResponseWriter, r *http.Request) {
 	//validate password using authreq.html
-	if !AuthValidateSecureRequest(w, r) {
+	if !AuthValidateSecureRequest(w, r, true) {
 		return
 	}
 
@@ -81,7 +81,7 @@ func hardware_power_poweroff(w http.ResponseWriter, r *http.Request) {
 
 func hardware_power_restart(w http.ResponseWriter, r *http.Request) {
 	//Validate password using authreq.html
-	if !AuthValidateSecureRequest(w, r) {
+	if !AuthValidateSecureRequest(w, r, true) {
 		return
 	}
 

+ 144 - 53
mod/fileservers/servers/samba/handlers.go

@@ -266,26 +266,42 @@ func (s *ShareManager) DelSambaUser(w http.ResponseWriter, r *http.Request) {
 
 // List all samba users info
 func (s *ShareManager) ListSambaUsers(w http.ResponseWriter, r *http.Request) {
+	type SimplifiedUserInfo struct {
+		UnixUsername string
+		Domain       string
+		IsArozOSUser bool
+	}
+	results := []*SimplifiedUserInfo{}
 	userInfo, err := s.ListSambaUsersInfo()
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
 	}
 
-	js, _ := json.Marshal(userInfo)
+	for _, thisUserInfo := range userInfo {
+		thisIsArozOSUser := s.UserHandler.GetAuthAgent().UserExists(strings.TrimSpace(thisUserInfo.UnixUsername))
+		results = append(results, &SimplifiedUserInfo{
+			UnixUsername: strings.TrimSpace(thisUserInfo.UnixUsername),
+			Domain:       thisUserInfo.Domain,
+			IsArozOSUser: thisIsArozOSUser,
+		})
+	}
+
+	js, _ := json.Marshal(results)
 	utils.SendJSONResponse(w, string(js))
 }
 
 // Activate a user account from arozos into samba user
-func (s *ShareManager) ActivateUserAccount(w http.ResponseWriter, r *http.Request, username string, password string) {
+func (s *ShareManager) ActivateUserAccount(w http.ResponseWriter, r *http.Request, password string) {
+	userInfo, _ := s.UserHandler.GetUserInfoFromRequest(w, r)
+
 	//Register this user to samba if not exists
-	sambaUserExists, err := s.SambaUserExists(username)
+	sambaUserExists, err := s.SambaUserExists(userInfo.Username)
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
 	}
 
-	userInfo, _ := s.UserHandler.GetUserInfoFromRequest(w, r)
 	if !sambaUserExists {
 		//This user account not activated yet. Activate it
 		err = s.AddSambaUser(userInfo.Username, password)
@@ -297,65 +313,140 @@ func (s *ShareManager) ActivateUserAccount(w http.ResponseWriter, r *http.Reques
 
 	//Create the user root share folders
 	for _, fsh := range userInfo.GetAllAccessibleFileSystemHandler() {
-		if fsh.IsLocalDrive() {
+		if fsh.IsNetworkDrive() {
 			//Samba can only work with drives locally hosted on this server
-			fshID := fsh.UUID
-			fshSharePath := fsh.Path
-			if fsh.RequierUserIsolation() {
-				//User seperated storage. Only mount the user one
-				fshID = fsh.UUID + "_" + userInfo.Username
-				fshSharePath = filepath.Join(fsh.Path, "/users/", userInfo.Username+"/")
-			}
+			//Skip network drives
+			continue
+		}
 
-			fshID = sanitizeShareName(fshID)
+		fshID := fsh.UUID
+		fshSharePath := fsh.Path
+		if fsh.RequierUserIsolation() {
+			//User seperated storage. Only mount the user one
+			fshID = userInfo.Username + "_" + fsh.UUID
+			fshSharePath = filepath.Join(fsh.Path, "/users/", userInfo.Username+"/")
+		}
+
+		fshID = sanitizeShareName(fshID)
 
-			//Check if the share already exists
-			shareExists, err := s.ShareExists(fshID)
+		//Check if the share already exists
+		shareExists, err := s.ShareExists(fshID)
+		if err != nil {
+			continue
+		}
+
+		if !shareExists {
+			//Try to create the share
+			fshShareAbsolutePath, err := filepath.Abs(fshSharePath)
 			if err != nil {
+				log.Println("[Samba] Unable to generate share config for path: " + fshSharePath)
+				continue
+			}
+
+			//Check if that folder exists
+			if !utils.FileExists(fshShareAbsolutePath) {
+				//Folder not exists. Continue
+				log.Println("[Samba] Path not exists for file system handler: " + fshSharePath)
 				continue
 			}
 
-			if !shareExists {
-				//Try to create the share
-				fshShareAbsolutePath, err := filepath.Abs(fshSharePath)
-				if err != nil {
-					log.Println("[Samba] Unable to generate share config for path: " + fshSharePath)
-					continue
-				}
-
-				//Check if that folder exists
-				if utils.FileExists(fshShareAbsolutePath) {
-					//Folder not exists. Continue
-					log.Println("[Samba] Path not exists for file system handler: " + fshSharePath)
-					continue
-				}
-
-				//Ok! Create the share with this username
-				err = s.CreateNewSambaShare(&ShareConfig{
-					Name:       fshID,
-					Path:       fshShareAbsolutePath,
-					ValidUsers: []string{userInfo.Username},
-					ReadOnly:   false,
-					Browseable: true,
-					GuestOk:    false,
-				})
-
-				if err != nil {
-					log.Println("[Samba] Failed to create share: " + err.Error())
-					utils.SendErrorResponse(w, err.Error())
-					return
-				}
-			} else {
-				//Share exists. Add this user to such share
-				err = s.AddUserToSambaShare(fshID, userInfo.Username)
-				if err != nil {
-					log.Println("[Samba] Failed to add user " + userInfo.Username + " to share " + fshID + ": " + err.Error())
-					utils.SendErrorResponse(w, err.Error())
-					return
-				}
+			//Ok! Create the share with this username
+			err = s.CreateNewSambaShare(&ShareConfig{
+				Name:       fshID,
+				Path:       fshShareAbsolutePath,
+				ValidUsers: []string{userInfo.Username},
+				ReadOnly:   false,
+				Browseable: !fsh.RequierUserIsolation(),
+				GuestOk:    false,
+			})
+
+			if err != nil {
+				log.Println("[Samba] Failed to create share: " + err.Error())
+				utils.SendErrorResponse(w, err.Error())
+				return
+			}
+		} else {
+			//Share exists. Add this user to such share
+			err = s.AddUserToSambaShare(fshID, userInfo.Username)
+			if err != nil {
+				log.Println("[Samba] Failed to add user " + userInfo.Username + " to share " + fshID + ": " + err.Error())
+				utils.SendErrorResponse(w, err.Error())
+				return
 			}
+		}
+
+	}
+
+	utils.SendOK(w)
+}
+
+// Get if the user share has been enabled
+func (s *ShareManager) HandleUserSmbStatusList(w http.ResponseWriter, r *http.Request) {
+	type UserStatus struct {
+		SmbdEnabled         bool
+		UserSmbShareEnabled bool
+		UserSmbShareList    []*ShareConfig
+	}
+
+	result := UserStatus{
+		SmbdEnabled:         s.IsEnabled(),
+		UserSmbShareEnabled: false,
+		UserSmbShareList:    []*ShareConfig{},
+	}
 
+	userInfo, err := s.UserHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+	userAccessibleShares, err := s.GetUsersShare(userInfo.Username)
+	if err != nil {
+		//User never used smb service
+		js, _ := json.Marshal(result)
+		utils.SendJSONResponse(w, string(js))
+		return
+	}
+
+	if len(userAccessibleShares) == 0 {
+		result.UserSmbShareEnabled = false
+	} else {
+		result.UserSmbShareEnabled = true
+		result.UserSmbShareList = userAccessibleShares
+	}
+
+	js, _ := json.Marshal(result)
+	utils.SendJSONResponse(w, string(js))
+}
+
+// Deactivate the user account by removing user access to all shares
+func (s *ShareManager) DeactiveUserAccount(w http.ResponseWriter, r *http.Request) {
+	userInfo, err := s.UserHandler.GetUserInfoFromRequest(w, r)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+	userAccessibleShares, err := s.GetUsersShare(userInfo.Username)
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//For each of the shares this user can access, remove his name from the share
+	for _, userAccessibleShare := range userAccessibleShares {
+		err = s.RemoveUserFromSambaShare(userAccessibleShare.Name, userInfo.Username)
+		if err != nil {
+			log.Println("[Samba] Unable to remove user " + userInfo.Username + " from share: " + err.Error())
+			continue
 		}
 	}
+
+	//Remove this samba user
+	err = s.RemoveSmbUser(userInfo.Username)
+	if err != nil {
+		utils.SendErrorResponse(w, "Samba user remove failed: "+err.Error())
+		return
+	}
+
 	utils.SendOK(w)
+
 }

+ 51 - 5
mod/fileservers/servers/samba/helpers.go

@@ -1,6 +1,7 @@
 package samba
 
 import (
+	"bytes"
 	"fmt"
 	"os/exec"
 	"path/filepath"
@@ -13,17 +14,46 @@ func convertShareConfigToString(share *ShareConfig) string {
 	var builder strings.Builder
 
 	builder.WriteString(fmt.Sprintf("\n[%s]\n", share.Name))
-	builder.WriteString(fmt.Sprintf("\tpath = %s\n", share.Path))
+	builder.WriteString(fmt.Sprintf("    path = %s\n", share.Path))
 	if len(share.ValidUsers) > 0 {
-		builder.WriteString(fmt.Sprintf("\tvalid users = %s\n", strings.Join(share.ValidUsers, " ")))
+		builder.WriteString(fmt.Sprintf("    valid users = %s\n", strings.Join(share.ValidUsers, " ")))
+	}
+	builder.WriteString(fmt.Sprintf("    read only = %s\n", boolToYesNo(share.ReadOnly)))
+	builder.WriteString(fmt.Sprintf("    browseable = %s\n", boolToYesNo(share.Browseable)))
+	builder.WriteString(fmt.Sprintf("    guest ok = %s\n", boolToYesNo(share.GuestOk)))
+	builder.WriteString("    create mask = 0644\n")
+	builder.WriteString("    directory mask = 0755\n")
+
+	folderOwner, err := getOwner(share.Path)
+	if err == nil {
+		builder.WriteString(fmt.Sprintf("    force user = %s\n", folderOwner))
 	}
-	builder.WriteString(fmt.Sprintf("\tread only = %s\n", boolToYesNo(share.ReadOnly)))
-	builder.WriteString(fmt.Sprintf("\tbrowseable = %s\n", boolToYesNo(share.Browseable)))
-	builder.WriteString(fmt.Sprintf("\tguest ok = %s\n", boolToYesNo(share.GuestOk)))
 
 	return builder.String()
 }
 
+// Get the folder owner for samba to know which user to use for accessing the folder permission
+func getOwner(folderPath string) (string, error) {
+	cmd := exec.Command("bash", "-c", fmt.Sprintf("ls -ld %s", folderPath))
+
+	var out bytes.Buffer
+	cmd.Stdout = &out
+
+	err := cmd.Run()
+	if err != nil {
+		return "", fmt.Errorf("failed to execute command: %v", err)
+	}
+
+	output := out.String()
+	fields := strings.Fields(output)
+	if len(fields) < 3 {
+		return "", fmt.Errorf("unexpected output format: %s", output)
+	}
+
+	owner := fields[2]
+	return owner, nil
+}
+
 // boolToYesNo converts a boolean to "yes" or "no"
 func boolToYesNo(value bool) string {
 	if value {
@@ -100,7 +130,23 @@ func sanitizeShareName(input string) string {
 			result.WriteRune(char)
 		} else if unicode.IsSpace(char) {
 			result.WriteRune(' ')
+		} else if char == '_' {
+			result.WriteRune('_')
+		} else if char == '-' {
+			result.WriteRune('-')
 		}
 	}
 	return result.String()
 }
+
+// Sometime the share name in file doesnt match for sharename in smb.conf
+// this function load the smb.conf and get the correct case for the name of the share
+// return empty string if not found
+func (s *ShareManager) getCorrectCaseForShareName(sharename string) string {
+	targetShare, err := s.GetShareByName(sharename)
+	if err != nil {
+		return ""
+	}
+
+	return targetShare.Name
+}

+ 112 - 55
mod/fileservers/servers/samba/samba.go

@@ -39,7 +39,7 @@ type ShareConfig struct {
 	GuestOk    bool
 }
 
-func NewSambaShareManager() (*ShareManager, error) {
+func NewSambaShareManager(userHandler *user.UserHandler) (*ShareManager, error) {
 	if runtime.GOOS == "linux" {
 		//Check if samba installed
 		if !utils.FileExists("/bin/smbcontrol") {
@@ -51,10 +51,11 @@ func NewSambaShareManager() (*ShareManager, error) {
 	return &ShareManager{
 		SambaConfigPath: "/etc/samba/smb.conf",
 		BackupDir:       "./backup",
+		UserHandler:     userHandler,
 	}, nil
 }
 
-// ReadSambaShares reads the smb.conf file and extracts all existing shares
+// ReadSambaShares reads  / lists the smb.conf file and extracts all existing shares
 func (s *ShareManager) ReadSambaShares() ([]ShareConfig, error) {
 	file, err := os.Open(s.SambaConfigPath)
 	if err != nil {
@@ -162,7 +163,7 @@ func (s *ShareManager) CreateNewSambaShare(shareToCreate *ShareConfig) error {
 	shareExists := false
 	shareNameSection := fmt.Sprintf("[%s]", shareToCreate.Name)
 	for scanner.Scan() {
-		if strings.TrimSpace(scanner.Text()) == shareNameSection {
+		if strings.EqualFold(strings.TrimSpace(scanner.Text()), shareNameSection) {
 			shareExists = true
 			break
 		}
@@ -196,6 +197,18 @@ func (s *ShareManager) CreateNewSambaShare(shareToCreate *ShareConfig) error {
 
 // RemoveSambaShareConfig removes the Samba share configuration from smb.conf
 func (s *ShareManager) RemoveSambaShareConfig(shareName string) error {
+	// Check if the share exists
+	shareExists, err := s.ShareExists(shareName)
+	if err != nil {
+		return err
+	}
+	if !shareExists {
+		return errors.New("share not exists")
+	}
+
+	//Convert the sharename to correct case for matching
+	shareName = s.getCorrectCaseForShareName(shareName)
+
 	// Open the smb.conf file for reading
 	file, err := os.Open(s.SambaConfigPath)
 	if err != nil {
@@ -219,7 +232,10 @@ func (s *ShareManager) RemoveSambaShareConfig(shareName string) error {
 		if strings.HasPrefix(line, "["+shareName+"]") {
 			// Skip the lines until the next section
 			for scanner.Scan() {
-				if strings.HasPrefix(scanner.Text(), "[") {
+				checkingLine := scanner.Text()
+				if strings.HasPrefix(checkingLine, "[") {
+					//The header of the next section is also need to be kept
+					fmt.Fprintln(tmpFile, checkingLine)
 					break
 				}
 			}
@@ -272,7 +288,7 @@ func (s *ShareManager) ShareExists(shareName string) (bool, error) {
 	scanner := bufio.NewScanner(file)
 	shareNameSection := fmt.Sprintf("[%s]", shareName)
 	for scanner.Scan() {
-		if strings.TrimSpace(scanner.Text()) == shareNameSection {
+		if strings.EqualFold(shareNameSection, strings.TrimSpace(scanner.Text())) {
 			return true, nil
 		}
 	}
@@ -325,77 +341,118 @@ func (s *ShareManager) BackupSmbConf() error {
 
 // Add a new user to smb.conf share by name
 func (s *ShareManager) AddUserToSambaShare(shareName, username string) error {
-	// Open the smb.conf file for reading
-	file, err := os.Open(s.SambaConfigPath)
+	targetShare, err := s.GetShareByName(shareName)
 	if err != nil {
-		return fmt.Errorf("failed to open smb.conf: %v", err)
+		return err
 	}
-	defer file.Close()
 
-	var lines []string
-	var insideShare bool
-	var shareExists bool
-	var userAdded bool
+	if s.UserCanAccessShare(targetShare, username) {
+		//User already have no access to this share, no need to delete
+		return nil
+	}
 
-	scanner := bufio.NewScanner(file)
-	for scanner.Scan() {
-		line := scanner.Text()
-		lines = append(lines, line)
+	//User not in this share. Append user name in this valid users
+	targetShare.ValidUsers = append(targetShare.ValidUsers, username)
 
-		// Check if we are inside the specified share section
-		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
-			if insideShare && shareExists && !userAdded {
-				// Add the username to the valid users list
-				lines = append(lines, "   valid users = @"+username)
-				userAdded = true
-			}
-			insideShare = false
-		}
+	//Delete the old one and create the new share with updated user list
+	err = s.RemoveSambaShareConfig(shareName)
+	if err != nil {
+		return err
+	}
 
-		if strings.TrimSpace(line) == fmt.Sprintf("[%s]", shareName) {
-			insideShare = true
-			shareExists = true
-		}
+	err = s.CreateNewSambaShare(targetShare)
+	if err != nil {
+		return err
+	}
 
-		if insideShare && strings.HasPrefix(strings.TrimSpace(line), "valid users =") {
-			// Check if the username already exists in the valid users list
-			validUsersLine := strings.TrimSpace(line)
-			if !strings.Contains(validUsersLine, username) {
-				lines[len(lines)-1] = validUsersLine + ", @" + username
-				userAdded = true
-			}
+	return nil
+}
+
+// Return if the user can access this samba share
+func (s *ShareManager) UserCanAccessShare(targetSmbShare *ShareConfig, username string) bool {
+	return utils.StringInArray(targetSmbShare.ValidUsers, username)
+}
+
+// Get a list of shares that this user have access to
+func (s *ShareManager) GetUsersShare(username string) ([]*ShareConfig, error) {
+	allShares, err := s.ReadSambaShares()
+	if err != nil {
+		return nil, err
+	}
+
+	userAccessibleShares := []*ShareConfig{}
+	for _, thisShare := range allShares {
+		if s.UserCanAccessShare(&thisShare, username) {
+			thisShareObject := thisShare
+			userAccessibleShares = append(userAccessibleShares, &thisShareObject)
 		}
 	}
 
-	if err := scanner.Err(); err != nil {
-		return fmt.Errorf("error reading smb.conf: %v", err)
+	return userAccessibleShares, nil
+}
+
+// Remove a user from smb.conf share by name
+func (s *ShareManager) RemoveUserFromSambaShare(shareName, username string) error {
+	targetShare, err := s.GetShareByName(shareName)
+	if err != nil {
+		return err
 	}
 
-	if !shareExists {
-		return fmt.Errorf("share [%s] not found in smb.conf", shareName)
+	if !s.UserCanAccessShare(targetShare, username) {
+		//User already have no access to this share, no need to delete
+		return nil
 	}
 
-	if !userAdded {
-		// If no valid users line was found, add the username to the share
-		lines = append(lines, fmt.Sprintf("[%s]", shareName))
-		lines = append(lines, "   valid users = @"+username)
+	if len(targetShare.ValidUsers) == 1 && strings.EqualFold(targetShare.ValidUsers[0], username) {
+		//This user is the only person who can access this share
+		//Remove the share entirely
+		//Delete the old one and create the new share with updated user list
+		err = s.RemoveSambaShareConfig(shareName)
+		if err != nil {
+			return err
+		}
+
+		return nil
 	}
 
-	// Write the updated configuration back to the smb.conf file
-	outputFile, err := os.Create(s.SambaConfigPath)
+	//User is in this share but this share contain other users.
+	err = s.RemoveSambaShareConfig(shareName)
 	if err != nil {
-		return fmt.Errorf("failed to open smb.conf for writing: %v", err)
+		return err
 	}
-	defer outputFile.Close()
 
-	writer := bufio.NewWriter(outputFile)
-	for _, line := range lines {
-		_, err := writer.WriteString(line + "\n")
-		if err != nil {
-			return fmt.Errorf("error writing to smb.conf: %v", err)
+	//Create a new valid user list
+	newShareValidUsers := []string{}
+	for _, validUser := range targetShare.ValidUsers {
+		if !strings.EqualFold(validUser, username) {
+			newShareValidUsers = append(newShareValidUsers, validUser)
 		}
 	}
-	writer.Flush()
+	targetShare.ValidUsers = newShareValidUsers
+
+	//Create the share
+	err = s.CreateNewSambaShare(targetShare)
+	if err != nil {
+		return err
+	}
 
 	return nil
 }
+
+// Get a share by name
+func (s *ShareManager) GetShareByName(shareName string) (*ShareConfig, error) {
+	allShares, err := s.ReadSambaShares()
+	if err != nil {
+		return nil, err
+
+	}
+
+	for _, thisShare := range allShares {
+		if strings.EqualFold(shareName, thisShare.Name) {
+			return &thisShare, nil
+		}
+	}
+
+	return nil, errors.New("target share not found")
+
+}

+ 17 - 1
network.go

@@ -344,7 +344,7 @@ func FileServerInit() {
 
 	//Samba
 	var err error
-	SambaShareManager, err = samba.NewSambaShareManager()
+	SambaShareManager, err = samba.NewSambaShareManager(userHandler)
 	if err != nil {
 		//Disable samba if not installed or platform not supported
 		log.Println("[INFO] Samba Share Manager Disabled: " + err.Error())
@@ -372,6 +372,22 @@ func FileServerInit() {
 	adminRouter.HandleFunc("/system/storage/ftp/passivemode", FTPManager.HandleFTPPassiveModeSettings)
 
 	//Samba Shares
+	//Activate and Deactivate are functions all users can use if admin enabled smbd service
+	router.HandleFunc("/system/storage/samba/activate", func(w http.ResponseWriter, r *http.Request) {
+		if !AuthValidateSecureRequest(w, r, false) {
+			return
+		}
+
+		if !SambaShareManager.IsEnabled() {
+			utils.SendErrorResponse(w, "smbd is not enabled on this server")
+			return
+		}
+		password, _ := utils.PostPara(r, "password")
+		SambaShareManager.ActivateUserAccount(w, r, password)
+	})
+	adminRouter.HandleFunc("/system/storage/samba/deactivate", SambaShareManager.DeactiveUserAccount)
+	adminRouter.HandleFunc("/system/storage/samba/myshare", SambaShareManager.HandleUserSmbStatusList)
+
 	adminRouter.HandleFunc("/system/storage/samba/status", SambaShareManager.SmbdStates)
 	adminRouter.HandleFunc("/system/storage/samba/list", SambaShareManager.ListSambaShares)
 	adminRouter.HandleFunc("/system/storage/samba/add", SambaShareManager.AddSambaShare)

+ 62 - 2
web/SystemAO/disk/instr/samba.html

@@ -9,5 +9,65 @@
     <small>Samba can only share local disks or partitions. Remote file system mounted into ArozOS cannot be shared by SMB.</small>
 </div>
 <p>Click "Activate" to enable SMB access to your file system</p>
-<button class="ui basic button"><i class="ui green circle check icon"></i> Activate</button>
-<button class="ui basic disabled button"><i class="ui red circle times icon"></i> Disable</button>
+<button class="ui basic activateSmb button" onclick="activateAccountForThisUser();"><i class="ui green circle check icon"></i> Activate</button>
+<button class="ui basic disableSmb disabled button"><i class="ui red circle times icon"></i> Disable</button>
+
+<script>
+    function initSMBActivationStatusOnThisUser(){
+        $.get("/system/storage/samba/myshare", function(data){
+            if (data.error != undefined || !data.SmbdEnabled){
+                $(".activateSmb").addClass("disabled");
+                $(".disableSmb").addClass("disabled");
+            }else{
+                if (data.UserSmbShareEnabled){
+                    $(".activateSmb").addClass("disabled");
+                    $(".disableSmb").removeClass("disabled");
+                }else{
+                    $(".activateSmb").removeClass("disabled");
+                    $(".disableSmb").addClass("disabled");
+                }
+            }
+        });
+    }
+    initSMBActivationStatusOnThisUser();
+
+    //Activate account for user, require password confirmation
+    function activateAccountForThisUser(){
+        var apiObject = {
+                api: "../system/storage/samba/activate",
+                data: {},
+                title: `Activate SMB Access`,
+                desc: `Confirm enable sharing my account using SMB (Samba)`,
+                thisuser: true, //This username as default, set to false for entering other user
+                method: "POST",
+                success: undefined
+            }
+            apiObject = encodeURIComponent(JSON.stringify(apiObject));
+            
+            
+            parent.newFloatWindow({
+                url: "SystemAO/security/authreq.html#" + apiObject,
+                width: 480,
+                height: 300,
+                appicon: "SystemAO/security/img/lock.svg",
+                title: `Activate SMB Access`,
+                parent: ao_module_windowID,
+                callback: "handleSMBActivateCallback"
+            });
+    }
+
+    window.handleSMBActivateCallback = function(data){
+        if (data.error != undefined){
+            msgbox(data.error, false, 6000);
+        }else{
+            if (data != false){
+                msgbox("SMB Share activated");
+            }
+            initSMBActivationStatusOnThisUser();
+
+            if (typeof(initShareListTable) != "undefined"){
+                initShareListTable();
+            }
+        }
+    }
+</script>

+ 6 - 4
web/SystemAO/disk/samba.html

@@ -5,6 +5,7 @@
     <label>Enable smbd (Samba Sharing Service)</label>
 </div>
 
+<div class="ui divider"></div>
 <h3><i class="ui green share alternate icon"></i> Samba Share Lists</h3>
 <p>A list of SMB shares currently written into smb.conf</p>
 <div style="width: 100%; overflow-y: auto;">
@@ -15,8 +16,7 @@
 </div>
 
 <!-- Create new Samba Share -->
-<div class="ui divider"></div>
-<h3><i class="ui green circle add icon"></i> Add Samba Share</h3>
+<h4><i class="ui green circle add icon"></i> Add Samba Share</h4>
 <p>Create a new SMB share folder from local disk</p>
 <form class="ui form" id="shareForm">
     <div class="field">
@@ -59,10 +59,11 @@
 </form>
 <!-- Create new Samba user -->
 <div class="ui divider"></div>
+<h3><i class="ui green user circle icon"></i> Samba Users List</h3>
 <p>Current list of users registered in Samba database</p>
 <div id="userTableContainer"></div>
 
-<h3><i class="ui green user plus icon"></i> Add Samba User</h3>
+<h4><i class="ui green user plus icon"></i> Add Samba User</h4>
 <p>Create Samba user for sharing<br>
 <small>Samba user is not ArozOS user. Creating a Samba user will also create a unix user with login function disabled</small></p>
 <div class="ui container">
@@ -284,6 +285,7 @@
                         <tr>
                             <th>Unix Username</th>
                             <th>Domain</th>
+                            <th>ArozOS User</th>
                             <th>Remove</th>
                         </tr>
                     </thead>
@@ -296,6 +298,7 @@
                     <tr>
                         <td><img class="ui avatar image" src="/system/users/profilepic?user=${item.UnixUsername}"> ${item.UnixUsername}</td>
                         <td>${item.Domain}</td>
+                        <td>${item.IsArozOSUser?"<i class='ui green check icon'></i>":"<i class='ui red times icon'></i>"}</td>
                         <td><button class="ui basic small red button" onclick="removeSambaUser('${item.UnixUsername}');"><i class="ui red trash icon"></i> Remove Samba User</button></td>
                     </tr>
                 `;
@@ -371,6 +374,5 @@
                     }
                 });
             }
-            
         }
 </script>