diff options
Diffstat (limited to 'weed/sftpd/user/homemanager.go')
| -rw-r--r-- | weed/sftpd/user/homemanager.go | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/weed/sftpd/user/homemanager.go b/weed/sftpd/user/homemanager.go new file mode 100644 index 000000000..c9051939c --- /dev/null +++ b/weed/sftpd/user/homemanager.go @@ -0,0 +1,204 @@ +package user + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/seaweedfs/seaweedfs/weed/glog" + filer_pb "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" + "github.com/seaweedfs/seaweedfs/weed/util" +) + +// HomeManager handles user home directory operations +type HomeManager struct { + filerClient FilerClient +} + +// FilerClient defines the interface for interacting with the filer +type FilerClient interface { + WithFilerClient(streamingMode bool, fn func(client filer_pb.SeaweedFilerClient) error) error + GetDataCenter() string + AdjustedUrl(location *filer_pb.Location) string +} + +// NewHomeManager creates a new home directory manager +func NewHomeManager(filerClient FilerClient) *HomeManager { + return &HomeManager{ + filerClient: filerClient, + } +} + +// EnsureHomeDirectory creates the user's home directory if it doesn't exist +func (hm *HomeManager) EnsureHomeDirectory(user *User) error { + if user.HomeDir == "" { + return fmt.Errorf("user has no home directory configured") + } + + glog.V(0).Infof("Ensuring home directory exists for user %s: %s", user.Username, user.HomeDir) + + // Check if home directory exists and create it if needed + err := hm.createDirectoryIfNotExists(user.HomeDir, user) + if err != nil { + return fmt.Errorf("failed to ensure home directory: %v", err) + } + + // Update user permissions map to include the home directory with full access if not already present + if user.Permissions == nil { + user.Permissions = make(map[string][]string) + } + + // Only add permissions if not already present + if _, exists := user.Permissions[user.HomeDir]; !exists { + user.Permissions[user.HomeDir] = []string{"all"} + glog.V(0).Infof("Added full permissions for user %s to home directory %s", + user.Username, user.HomeDir) + } + + return nil +} + +// createDirectoryIfNotExists creates a directory path if it doesn't exist +func (hm *HomeManager) createDirectoryIfNotExists(dirPath string, user *User) error { + // Split the path into components + components := strings.Split(strings.Trim(dirPath, "/"), "/") + currentPath := "/" + + for _, component := range components { + if component == "" { + continue + } + + nextPath := filepath.Join(currentPath, component) + err := hm.createSingleDirectory(nextPath, user) + if err != nil { + return err + } + + currentPath = nextPath + } + + return nil +} + +// createSingleDirectory creates a single directory if it doesn't exist +func (hm *HomeManager) createSingleDirectory(dirPath string, user *User) error { + var dirExists bool + + err := hm.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + dir, name := util.FullPath(dirPath).DirAndName() + + // Check if directory exists + resp, err := client.LookupDirectoryEntry(ctx, &filer_pb.LookupDirectoryEntryRequest{ + Directory: dir, + Name: name, + }) + + if err != nil || resp.Entry == nil { + // Directory doesn't exist, create it + glog.V(0).Infof("Creating directory %s for user %s", dirPath, user.Username) + + err = filer_pb.Mkdir(hm, string(dir), name, func(entry *filer_pb.Entry) { + // Set appropriate permissions + entry.Attributes.FileMode = uint32(0700 | os.ModeDir) // rwx------ for user + entry.Attributes.Uid = user.Uid + entry.Attributes.Gid = user.Gid + + // Set creation and modification times + now := time.Now().Unix() + entry.Attributes.Crtime = now + entry.Attributes.Mtime = now + + // Add extended attributes + if entry.Extended == nil { + entry.Extended = make(map[string][]byte) + } + entry.Extended["creator"] = []byte(user.Username) + entry.Extended["auto_created"] = []byte("true") + }) + + if err != nil { + return fmt.Errorf("failed to create directory %s: %v", dirPath, err) + } + } else if !resp.Entry.IsDirectory { + return fmt.Errorf("path %s exists but is not a directory", dirPath) + } else { + dirExists = true + + // Update ownership if needed + if resp.Entry.Attributes.Uid != user.Uid || resp.Entry.Attributes.Gid != user.Gid { + glog.V(0).Infof("Updating ownership of directory %s for user %s", dirPath, user.Username) + + entry := resp.Entry + entry.Attributes.Uid = user.Uid + entry.Attributes.Gid = user.Gid + + _, updateErr := client.UpdateEntry(ctx, &filer_pb.UpdateEntryRequest{ + Directory: dir, + Entry: entry, + }) + + if updateErr != nil { + glog.Warningf("Failed to update directory ownership: %v", updateErr) + } + } + } + + return nil + }) + + if err != nil { + return err + } + + if !dirExists { + // Verify the directory was created + verifyErr := hm.filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + dir, name := util.FullPath(dirPath).DirAndName() + resp, err := client.LookupDirectoryEntry(ctx, &filer_pb.LookupDirectoryEntryRequest{ + Directory: dir, + Name: name, + }) + + if err != nil || resp.Entry == nil { + return fmt.Errorf("directory not found after creation") + } + + if !resp.Entry.IsDirectory { + return fmt.Errorf("path exists but is not a directory") + } + + dirExists = true + return nil + }) + + if verifyErr != nil { + return fmt.Errorf("failed to verify directory creation: %v", verifyErr) + } + } + + return nil +} + +// Implement necessary methods to satisfy the filer_pb.FilerClient interface +func (hm *HomeManager) AdjustedUrl(location *filer_pb.Location) string { + return hm.filerClient.AdjustedUrl(location) +} + +func (hm *HomeManager) GetDataCenter() string { + return hm.filerClient.GetDataCenter() +} + +// WithFilerClient delegates to the underlying filer client +func (hm *HomeManager) WithFilerClient(streamingMode bool, fn func(client filer_pb.SeaweedFilerClient) error) error { + return hm.filerClient.WithFilerClient(streamingMode, fn) +} |
