8003db49b3
Samba shares now support an 'invalid users' list (deny list), written as 'invalid users = u1,u2' in smb.conf. The UI shows a ChipPicker for valid_users and invalid_users, mutually exclusive, sourced from the system user list. feat: add ImportSystemUsers for fresh installations When NASCTL_IMPORT_ON_BOOT=true, nasctl now imports existing system users from /etc/passwd (UID 1000-60000) and /etc/group (supplemental groups), and detects which have Samba accounts via 'pdbedit -L'. Imported users are marked dirty so the admin can review before applying. New POST /api/import/users endpoint for manual re-import. This mirrors the existing import-on-boot flow for smb.conf and /etc/exports.
184 lines
4.7 KiB
Go
184 lines
4.7 KiB
Go
package importer
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/darroyo/nasctl/internal/db"
|
|
"github.com/darroyo/nasctl/internal/modules/nfs"
|
|
"github.com/darroyo/nasctl/internal/modules/samba"
|
|
"github.com/darroyo/nasctl/internal/modules/users"
|
|
)
|
|
|
|
type ImportResult struct {
|
|
SambaImported int
|
|
NFSImported int
|
|
UsersImported int
|
|
SambaSkipped bool
|
|
NFSSkipped bool
|
|
UsersSkipped bool
|
|
SambaError string
|
|
NFSError string
|
|
UsersError string
|
|
}
|
|
|
|
type ImporterDB interface {
|
|
ListSambaShares() ([]db.SambaShare, error)
|
|
ListNFSExports() ([]db.NFSExport, error)
|
|
ListUsers() ([]db.User, error)
|
|
ReplaceSambaShares(shares []db.SambaShare) error
|
|
ReplaceNFSExports(exports []db.NFSExport) error
|
|
ReplaceUsers(users []db.User) error
|
|
GetSetting(key string) (string, bool, error)
|
|
SetSetting(key, value string) error
|
|
MarkDirty(module string) error
|
|
}
|
|
|
|
func ImportOnBoot(ctx context.Context, database ImporterDB, smbPath, exportsPath, adminUsername string) ImportResult {
|
|
result := ImportResult{}
|
|
|
|
sambaDone, _, _ := database.GetSetting("import.samba.done")
|
|
nfsDone, _, _ := database.GetSetting("import.nfs.done")
|
|
usersDone, _, _ := database.GetSetting("import.users.done")
|
|
|
|
allDone := sambaDone == "true" && nfsDone == "true" && usersDone == "true"
|
|
if allDone {
|
|
return result
|
|
}
|
|
|
|
if sambaDone != "true" {
|
|
sr := importSamba(ctx, database, smbPath)
|
|
result.SambaImported = sr.count
|
|
result.SambaSkipped = sr.skipped
|
|
result.SambaError = sr.err
|
|
if sr.err == "" {
|
|
_ = database.SetSetting("import.samba.done", "true")
|
|
}
|
|
}
|
|
|
|
if nfsDone != "true" {
|
|
nr := importNFS(ctx, database, exportsPath)
|
|
result.NFSImported = nr.count
|
|
result.NFSSkipped = nr.skipped
|
|
result.NFSError = nr.err
|
|
if nr.err == "" {
|
|
_ = database.SetSetting("import.nfs.done", "true")
|
|
}
|
|
}
|
|
|
|
if usersDone != "true" {
|
|
ur := importUsers(ctx, database, adminUsername)
|
|
result.UsersImported = ur.count
|
|
result.UsersSkipped = ur.skipped
|
|
result.UsersError = ur.err
|
|
if ur.err == "" {
|
|
_ = database.SetSetting("import.users.done", "true")
|
|
}
|
|
}
|
|
|
|
if result.SambaImported > 0 || result.NFSImported > 0 || result.UsersImported > 0 {
|
|
log.Printf("[importer] imported %d samba shares, %d nfs exports, %d users",
|
|
result.SambaImported, result.NFSImported, result.UsersImported)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func ResetImportFlags(ctx context.Context, database ImporterDB) error {
|
|
_ = database.SetSetting("import.samba.done", "")
|
|
_ = database.SetSetting("import.nfs.done", "")
|
|
_ = database.SetSetting("import.users.done", "")
|
|
return nil
|
|
}
|
|
|
|
type importStep struct {
|
|
count int
|
|
skipped bool
|
|
err string
|
|
}
|
|
|
|
func importSamba(ctx context.Context, database ImporterDB, path string) importStep {
|
|
shares, err := ImportSambaShares(path)
|
|
if err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
if shares == nil {
|
|
return importStep{skipped: true}
|
|
}
|
|
|
|
existing, err := database.ListSambaShares()
|
|
if err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
if len(existing) > 0 {
|
|
return importStep{skipped: true}
|
|
}
|
|
|
|
if err := database.ReplaceSambaShares(shares); err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
|
|
if err := database.MarkDirty(samba.ModuleName); err != nil {
|
|
return importStep{count: len(shares), err: fmt.Sprintf("imported but could not mark dirty: %v", err)}
|
|
}
|
|
|
|
return importStep{count: len(shares)}
|
|
}
|
|
|
|
func importNFS(ctx context.Context, database ImporterDB, path string) importStep {
|
|
exports, err := ImportNFSExports(path)
|
|
if err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
if exports == nil {
|
|
return importStep{skipped: true}
|
|
}
|
|
|
|
existing, err := database.ListNFSExports()
|
|
if err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
if len(existing) > 0 {
|
|
return importStep{skipped: true}
|
|
}
|
|
|
|
if err := database.ReplaceNFSExports(exports); err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
|
|
if err := database.MarkDirty(nfs.ModuleName); err != nil {
|
|
return importStep{count: len(exports), err: fmt.Sprintf("imported but could not mark dirty: %v", err)}
|
|
}
|
|
|
|
return importStep{count: len(exports)}
|
|
}
|
|
|
|
func importUsers(ctx context.Context, database ImporterDB, adminUsername string) importStep {
|
|
imported, err := ImportSystemUsers(ctx, adminUsername)
|
|
if err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
if imported == nil {
|
|
return importStep{skipped: true}
|
|
}
|
|
|
|
existing, err := database.ListUsers()
|
|
if err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
if len(existing) > 0 {
|
|
return importStep{skipped: true}
|
|
}
|
|
|
|
if err := database.ReplaceUsers(imported); err != nil {
|
|
return importStep{err: err.Error()}
|
|
}
|
|
|
|
if err := database.MarkDirty(users.ModuleName); err != nil {
|
|
return importStep{count: len(imported), err: fmt.Sprintf("imported but could not mark dirty: %v", err)}
|
|
}
|
|
|
|
return importStep{count: len(imported)}
|
|
}
|