fix dir perms
This commit is contained in:
@@ -27,6 +27,7 @@ The program:
|
|||||||
- If user creation fails because admin rights are needed, explains the issue and offers to retry with `sudo`.
|
- If user creation fails because admin rights are needed, explains the issue and offers to retry with `sudo`.
|
||||||
- After creating a user, offers to launch `smbpasswd -a <user>` immediately so the Samba password can be set right away.
|
- After creating a user, offers to launch `smbpasswd -a <user>` immediately so the Samba password can be set right away.
|
||||||
- Before saving, checks whether each share folder exists and offers to create missing directories.
|
- Before saving, checks whether each share folder exists and offers to create missing directories.
|
||||||
|
- Before saving, offers to adjust share-folder ownership and permissions so connected users can actually read and write files.
|
||||||
- If saving the Samba config or its backup needs admin rights, explains the issue and offers to retry with `sudo`.
|
- If saving the Samba config or its backup needs admin rights, explains the issue and offers to retry with `sudo`.
|
||||||
- Writes a timestamped backup before saving changes.
|
- Writes a timestamped backup before saving changes.
|
||||||
|
|
||||||
|
|||||||
124
app.go
124
app.go
@@ -273,6 +273,9 @@ func (a *App) ensureShareDirectories(doc *Document) error {
|
|||||||
info, err := os.Stat(cfg.Path)
|
info, err := os.Stat(cfg.Path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
|
if err := a.offerAdjustDirectoryPermissions(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return fmt.Errorf("share %q path exists but is not a directory: %s", cfg.Name, cfg.Path)
|
return fmt.Errorf("share %q path exists but is not a directory: %s", cfg.Name, cfg.Path)
|
||||||
@@ -301,6 +304,9 @@ func (a *App) ensureShareDirectories(doc *Document) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.printf("Created folder %s\n", cfg.Path)
|
a.printf("Created folder %s\n", cfg.Path)
|
||||||
|
if err := a.offerAdjustDirectoryPermissions(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -693,6 +699,124 @@ func (a *App) createDirectory(path string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) offerAdjustDirectoryPermissions(cfg ShareConfig) error {
|
||||||
|
mode, owner, group, explanation := recommendSharePermissions(cfg)
|
||||||
|
|
||||||
|
a.println("")
|
||||||
|
a.printf("The share folder %s may need permissions adjusted for file operations.\n", cfg.Path)
|
||||||
|
a.println(explanation)
|
||||||
|
if owner != "" || group != "" {
|
||||||
|
a.printf("Suggested owner/group: %s:%s\n", defaultString(owner, "unchanged"), defaultString(group, "unchanged"))
|
||||||
|
}
|
||||||
|
a.printf("Suggested permissions: %s\n", mode)
|
||||||
|
a.println("I can apply these recommended permissions now.")
|
||||||
|
a.flush()
|
||||||
|
|
||||||
|
adjust, err := a.confirm("Adjust this folder now", true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !adjust {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.applyDirectoryPermissions(cfg.Path, owner, group, mode); err != nil {
|
||||||
|
return fmt.Errorf("adjust permissions for share %q: %w", cfg.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.printf("Updated permissions for %s\n", cfg.Path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func recommendSharePermissions(cfg ShareConfig) (mode, owner, group, explanation string) {
|
||||||
|
mode = "0770"
|
||||||
|
group = "users"
|
||||||
|
explanation = "Members of the allowed account list should usually be able to read and write here."
|
||||||
|
|
||||||
|
if strings.EqualFold(normalizeBoolish(cfg.ReadOnly, "no"), "yes") {
|
||||||
|
mode = "0755"
|
||||||
|
explanation = "This share is marked read-only, so a safer folder mode is recommended."
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.EqualFold(normalizeBoolish(cfg.GuestOK, "no"), "yes") {
|
||||||
|
mode = "0777"
|
||||||
|
group = ""
|
||||||
|
explanation = "This share allows guests, so wider permissions are usually needed for uploads and edits."
|
||||||
|
return mode, "", group, explanation
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.ValidUsers) == 1 {
|
||||||
|
owner = cfg.ValidUsers[0]
|
||||||
|
group = cfg.ValidUsers[0]
|
||||||
|
explanation = "This share is limited to one account, so making that account the owner is the simplest setup."
|
||||||
|
return mode, owner, group, explanation
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.ValidUsers) > 1 {
|
||||||
|
explanation = "This share has multiple allowed accounts, so a shared writable group-style setup is recommended."
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode, owner, group, explanation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) applyDirectoryPermissions(path, owner, group, mode string) error {
|
||||||
|
chownTarget := owner
|
||||||
|
if group != "" {
|
||||||
|
if chownTarget == "" {
|
||||||
|
chownTarget = ":" + group
|
||||||
|
} else {
|
||||||
|
chownTarget = chownTarget + ":" + group
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.runPrivilegedOrLocal("chmod", []string{mode, path}, "Changing folder permissions needs administrator permission."); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if chownTarget != "" {
|
||||||
|
if err := a.runPrivilegedOrLocal("chown", []string{chownTarget, path}, "Changing folder ownership needs administrator permission."); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) runPrivilegedOrLocal(command string, args []string, privilegeMessage string) error {
|
||||||
|
if err := a.runner.Run(command, args...); err == nil {
|
||||||
|
return nil
|
||||||
|
} else if shouldOfferPrivilegeRetry(err) {
|
||||||
|
a.println("")
|
||||||
|
a.println(privilegeMessage)
|
||||||
|
a.println("I can try again using sudo so you can enter your admin password.")
|
||||||
|
a.flush()
|
||||||
|
|
||||||
|
canUseSudo := false
|
||||||
|
if a.lookPath != nil {
|
||||||
|
_, sudoErr := a.lookPath("sudo")
|
||||||
|
canUseSudo = sudoErr == nil
|
||||||
|
}
|
||||||
|
if !canUseSudo {
|
||||||
|
return fmt.Errorf("%s %s: %w", command, strings.Join(args, " "), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
retry, promptErr := a.confirm("Retry with sudo", true)
|
||||||
|
if promptErr != nil {
|
||||||
|
return promptErr
|
||||||
|
}
|
||||||
|
if !retry {
|
||||||
|
return fmt.Errorf("%s %s: %w", command, strings.Join(args, " "), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sudoErr := a.runner.Run("sudo", append([]string{command}, args...)...); sudoErr != nil {
|
||||||
|
return fmt.Errorf("%s %s with sudo: %w", command, strings.Join(args, " "), sudoErr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("%s %s: %w", command, strings.Join(args, " "), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func friendlyWriteError(action, path string, err error) error {
|
func friendlyWriteError(action, path string, err error) error {
|
||||||
if errors.Is(err, os.ErrPermission) || strings.Contains(strings.ToLower(err.Error()), "permission denied") {
|
if errors.Is(err, os.ErrPermission) || strings.Contains(strings.ToLower(err.Error()), "permission denied") {
|
||||||
return fmt.Errorf("%s %s: administrator permission is needed", action, path)
|
return fmt.Errorf("%s %s: administrator permission is needed", action, path)
|
||||||
|
|||||||
BIN
samba-configer
BIN
samba-configer
Binary file not shown.
Reference in New Issue
Block a user