mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-11-03 13:11:46 +08:00
fix (plg_backend_nfs): support auxiliary gids
This commit is contained in:
@ -9,7 +9,10 @@ import (
|
|||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cacheForEtc AppCache
|
var (
|
||||||
|
cacheForEtc AppCache
|
||||||
|
cacheForGroup AppCache
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DEFAULT_UID = 1000
|
DEFAULT_UID = 1000
|
||||||
@ -18,28 +21,35 @@ const (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cacheForEtc = NewAppCache(120, 60)
|
cacheForEtc = NewAppCache(120, 60)
|
||||||
|
cacheForGroup = NewAppCache(120, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUid(hint string) uint32 {
|
func extractUserInfo(uidHint string, gidHint string, gidsHint string) (uint32, uint32, []uint32) {
|
||||||
if hint == "" {
|
// case 1: everything is being sent as "uid=number, gid=number and gids=number,number,number"
|
||||||
return DEFAULT_UID
|
if _uid, err := strconv.Atoi(uidHint); err == nil {
|
||||||
} else if _uid, err := strconv.Atoi(hint); err == nil {
|
var (
|
||||||
return uint32(_uid)
|
uid uint32 = uint32(_uid)
|
||||||
} else if uid, _, err := extractFromEtcPasswd(hint); err == nil {
|
gid uint32
|
||||||
return uid
|
gids []uint32
|
||||||
|
)
|
||||||
|
if _gid, err := strconv.Atoi(gidHint); err == nil {
|
||||||
|
gid = uint32(_gid)
|
||||||
|
} else {
|
||||||
|
gid = uid
|
||||||
}
|
}
|
||||||
return DEFAULT_UID
|
for _, t := range strings.Split(gidsHint, ",") {
|
||||||
|
if gid, err := strconv.Atoi(strings.TrimSpace(t)); err == nil {
|
||||||
|
gids = append(gids, uint32(gid))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGid(hint string) uint32 {
|
|
||||||
if hint == "" {
|
|
||||||
return DEFAULT_UID
|
|
||||||
} else if _gid, err := strconv.Atoi(hint); err == nil {
|
|
||||||
return uint32(_gid)
|
|
||||||
} else if _, gid, err := extractFromEtcPasswd(hint); err == nil {
|
|
||||||
return gid
|
|
||||||
}
|
}
|
||||||
return DEFAULT_GID
|
return uid, gid, gids
|
||||||
|
}
|
||||||
|
// case 2: auto detect everything, aka "uid=www-data gid=www-data gids=..." based on uid=www-data
|
||||||
|
if _uid, _gid, err := extractFromEtcPasswd(uidHint); err == nil {
|
||||||
|
return _uid, _gid, extractFromEtcGroup(uidHint, _gid)
|
||||||
|
}
|
||||||
|
// case 3: base case
|
||||||
|
return DEFAULT_UID, DEFAULT_GID, []uint32{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractFromEtcPasswd(username string) (uint32, uint32, error) {
|
func extractFromEtcPasswd(username string) (uint32, uint32, error) {
|
||||||
@ -61,7 +71,9 @@ func extractFromEtcPasswd(username string) (uint32, uint32, error) {
|
|||||||
s := strings.Split(string(line), ":")
|
s := strings.Split(string(line), ":")
|
||||||
if len(s) != 7 {
|
if len(s) != 7 {
|
||||||
continue
|
continue
|
||||||
} else if username == s[0] {
|
} else if username != s[0] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
u, err := strconv.Atoi(s[2])
|
u, err := strconv.Atoi(s[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@ -73,6 +85,38 @@ func extractFromEtcPasswd(username string) (uint32, uint32, error) {
|
|||||||
cacheForEtc.Set(map[string]string{"username": username}, []int{u, g})
|
cacheForEtc.Set(map[string]string{"username": username}, []int{u, g})
|
||||||
return uint32(u), uint32(g), nil
|
return uint32(u), uint32(g), nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return DEFAULT_UID, DEFAULT_GID, ErrNotFound
|
return DEFAULT_UID, DEFAULT_GID, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractFromEtcGroup(username string, primary uint32) []uint32 {
|
||||||
|
if v := cacheForGroup.Get(map[string]string{"username": username}); v != nil {
|
||||||
|
return v.([]uint32)
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile("/etc/group", os.O_RDONLY, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return []uint32{}
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
gids := []uint32{}
|
||||||
|
lines := bufio.NewReader(f)
|
||||||
|
for {
|
||||||
|
line, _, err := lines.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s := strings.Split(string(line), ":")
|
||||||
|
if len(s) != 4 {
|
||||||
|
continue
|
||||||
|
} else if username != s[3] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if gid, err := strconv.Atoi(s[2]); err == nil {
|
||||||
|
ugid := uint32(gid)
|
||||||
|
if ugid != primary {
|
||||||
|
gids = append(gids, ugid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cacheForGroup.Set(map[string]string{"username": username}, gids)
|
||||||
|
}
|
||||||
|
return gids
|
||||||
|
}
|
||||||
|
|||||||
@ -9,26 +9,28 @@ import (
|
|||||||
"github.com/vmware/go-nfs-client/nfs/xdr"
|
"github.com/vmware/go-nfs-client/nfs/xdr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ref: https://datatracker.ietf.org/doc/html/rfc5531#section-8.2
|
||||||
|
// so far we only have implemented AUTH_SYS but one day we might want to add support
|
||||||
|
// for RPCSEC_GSS as detailed in https://datatracker.ietf.org/doc/html/rfc2203
|
||||||
type AuthUnix struct {
|
type AuthUnix struct {
|
||||||
Stamp uint32
|
Stamp uint32
|
||||||
Machinename string
|
Machinename string
|
||||||
Uid uint32
|
Uid uint32
|
||||||
Gid uint32
|
Gid uint32
|
||||||
GidLen uint32
|
|
||||||
Gids []uint32
|
Gids []uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuth(machineName string, uid, gid uint32) rpc.Auth {
|
func NewUnixAuth(machineName string, uid, gid uint32, gids []uint32) rpc.Auth {
|
||||||
w := new(bytes.Buffer)
|
w := new(bytes.Buffer)
|
||||||
xdr.Write(w, AuthUnix{
|
xdr.Write(w, AuthUnix{
|
||||||
Stamp: rand.New(rand.NewSource(time.Now().UnixNano())).Uint32(),
|
Stamp: rand.New(rand.NewSource(time.Now().UnixNano())).Uint32(),
|
||||||
Machinename: machineName,
|
Machinename: machineName,
|
||||||
Uid: uid,
|
Uid: 1000,
|
||||||
Gid: gid,
|
Gid: 1000,
|
||||||
GidLen: 1,
|
Gids: gids,
|
||||||
})
|
})
|
||||||
return rpc.Auth{
|
return rpc.Auth{
|
||||||
1,
|
1, // = AUTH_SYS in RFC5531
|
||||||
w.Bytes(),
|
w.Bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,18 +33,16 @@ func (this NfsShare) Init(params map[string]string, app *App) (IBackend, error)
|
|||||||
if params["hostname"] == "" {
|
if params["hostname"] == "" {
|
||||||
return nil, ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if params["machine_name"] == "" {
|
if params["machine_name"] == "" {
|
||||||
params["machine_name"] = "filestash"
|
params["machine_name"] = "Filestash"
|
||||||
}
|
}
|
||||||
|
uid, gid, gids := extractUserInfo(params["uid"], params["gid"], params["gids"])
|
||||||
|
|
||||||
uid := getUid(params["uid"])
|
|
||||||
gid := getGid(params["gid"])
|
|
||||||
mount, err := nfs.DialMount(params["hostname"])
|
mount, err := nfs.DialMount(params["hostname"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
auth := NewAuth(params["machine_name"], uid, gid)
|
auth := NewUnixAuth(params["machine_name"], uid, gid, gids)
|
||||||
v, err := mount.Mount(
|
v, err := mount.Mount(
|
||||||
params["target"],
|
params["target"],
|
||||||
auth,
|
auth,
|
||||||
@ -77,31 +75,37 @@ func (this NfsShare) LoginForm() Form {
|
|||||||
Name: "advanced",
|
Name: "advanced",
|
||||||
Type: "enable",
|
Type: "enable",
|
||||||
Placeholder: "Advanced",
|
Placeholder: "Advanced",
|
||||||
Target: []string{"nfs_uid", "nfs_gid", "nfs_machinename", "nfs_chroot"},
|
Target: []string{"nfs_uid", "nfs_gid", "nfs_gids", "nfs_machinename", "nfs_chroot"},
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
Id: "nfs_uid",
|
Id: "nfs_uid",
|
||||||
Name: "uid",
|
Name: "uid",
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Placeholder: "uid",
|
Placeholder: "UID",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
Id: "nfs_gid",
|
Id: "nfs_gid",
|
||||||
Name: "gid",
|
Name: "gid",
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Placeholder: "gid",
|
Placeholder: "GID",
|
||||||
|
},
|
||||||
|
FormElement{
|
||||||
|
Id: "nfs_gids",
|
||||||
|
Name: "gids",
|
||||||
|
Type: "text",
|
||||||
|
Placeholder: "Auxiliary GIDs",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
Id: "nfs_machinename",
|
Id: "nfs_machinename",
|
||||||
Name: "machine_name",
|
Name: "machine_name",
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Placeholder: "machine name",
|
Placeholder: "Machine Name",
|
||||||
},
|
},
|
||||||
FormElement{
|
FormElement{
|
||||||
Id: "nfs_chroot",
|
Id: "nfs_chroot",
|
||||||
Name: "path",
|
Name: "path",
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Placeholder: "chroot",
|
Placeholder: "Chroot",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user