Skip to content

Commit

Permalink
feat: add option to control directory index
Browse files Browse the repository at this point in the history
- `--index`
- `--index-user`
- `--index-dir`
- `--index-dir-user`
  • Loading branch information
marjune163 committed May 12, 2024
1 parent d7ca8db commit faf4fb0
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 18 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ ghfs [options]
--auth-dir-user <separator><fs-path>[<separator><allowed-username>...] ...
Use Basic Auth for specific file system paths(and sub paths).
--index <url-path> ...
--index-user <separator><url-path>[<separator><allowed-username>...] ...
Set url paths(and sub paths) that allows to index files of a directory.
--index defaults to "/".
Set to "" to disable index.
--index-dir <fs-path> ...
--index-dir-user <separator><fs-path>[<separator><allowed-username>...] ...
Similar to --index, but use file system path instead of url path.
-U|--global-upload
Allow upload files for all url paths.
Use it with care.
Expand Down
9 changes: 9 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@ ghfs [选项]
--auth-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ...
对指定文件系统路径(及子路径)启用http基本验证。
--index <URL路径> ...
--index-user <分隔符><URL路径>[<分隔符><允许的用户名>...] ...
设置允许索引目录的URL路径(及子路径)。
--index默认值为"/"。
设为""来禁用索引。
--index-dir <文件系统路径> ...
--index-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ...
与--index类似,但指定的是文件系统路径,而不是URL路径。
-U|--global-upload
对所有URL路径开启上传权限。
请谨慎使用。
Expand Down
23 changes: 22 additions & 1 deletion src/param/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ func NewCliCmd() *goNixArgParser.Command {
err = options.AddFlagValues("authdirsusers", "--auth-dir-user", "", nil, "file system path that require Basic Auth for specific users, <sep><fs-path>[<sep><user>...]")
serverError.CheckFatal(err)

err = options.AddFlagValues("indexurls", "--index", "", []string{"/"}, "url path that allow directory index")
serverError.CheckFatal(err)

err = options.AddFlagValues("indexurlsusers", "--index-user", "", nil, "url path that allow index files for specific users, <sep><url-path>[<sep><user>...]")
serverError.CheckFatal(err)

err = options.AddFlagValues("indexdirs", "--index-dir", "", nil, "file system path that allow index files")
serverError.CheckFatal(err)

err = options.AddFlagValues("indexdirsusers", "--index-dir-user", "", nil, "file system path that allow index files for specific users, <sep><fs-path>[<sep><user>...]")
serverError.CheckFatal(err)

err = options.AddFlags("globalupload", []string{"-U", "--global-upload"}, "", "allow upload files for all url paths")
serverError.CheckFatal(err)

Expand Down Expand Up @@ -348,11 +360,14 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e
arrUsersSha512, _ := result.GetStrings("userssha512")
param.UsersSha512 = entriesToUsers(arrUsersSha512)

// auth/upload/mkdir/delete/archive/cors urls/dirs
// auth/index/upload/mkdir/delete/archive/cors urls/dirs
param.GlobalAuth = result.HasKey("globalauth")
param.AuthUrls, _ = result.GetStrings("authurls")
param.AuthDirs, _ = result.GetStrings("authdirs")

param.IndexUrls, _ = result.GetStrings("indexurls")
param.IndexDirs, _ = result.GetStrings("indexdirs")

param.GlobalUpload = result.HasKey("globalupload")
param.UploadUrls, _ = result.GetStrings("uploadurls")
param.UploadDirs, _ = result.GetStrings("uploaddirs")
Expand Down Expand Up @@ -380,6 +395,12 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e
authDirsUsers, _ := result.GetStrings("authdirsusers")
param.AuthDirsUsers = SplitAllKeyValues(authDirsUsers)

indexUrlsUsers, _ := result.GetStrings("indexurlsusers")
param.IndexUrlsUsers = SplitAllKeyValues(indexUrlsUsers)

indexDirsUsers, _ := result.GetStrings("indexdirsusers")
param.IndexDirsUsers = SplitAllKeyValues(indexDirsUsers)

uploadUrlsUsers, _ := result.GetStrings("uploadurlsusers")
param.UploadUrlsUsers = SplitAllKeyValues(uploadUrlsUsers)

Expand Down
24 changes: 22 additions & 2 deletions src/param/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ type Param struct {
HeadersUrls [][]string
HeadersDirs [][]string

IndexUrls []string
IndexUrlsUsers [][]string // [][path, user...]
IndexDirs []string
IndexDirsUsers [][]string // [][path, user...]

GlobalUpload bool
UploadUrls []string
UploadUrlsUsers [][]string // [][path, user...]
Expand Down Expand Up @@ -144,9 +149,11 @@ func (param *Param) normalize() (errs []error) {
// dir indexes
param.DirIndexes = normalizeFilenames(param.DirIndexes)

// auth/upload/mkdir/delete/archive/cors urls/dirs
// auth/index/upload/mkdir/delete/archive/cors urls/dirs
param.AuthUrls = NormalizeUrlPaths(param.AuthUrls)
param.AuthDirs = NormalizeFsPaths(param.AuthDirs)
param.IndexUrls = NormalizeUrlPaths(param.IndexUrls)
param.IndexDirs = NormalizeFsPaths(param.IndexDirs)
param.UploadUrls = NormalizeUrlPaths(param.UploadUrls)
param.UploadDirs = NormalizeFsPaths(param.UploadDirs)
param.MkdirUrls = NormalizeUrlPaths(param.MkdirUrls)
Expand All @@ -158,7 +165,7 @@ func (param *Param) normalize() (errs []error) {
param.CorsUrls = NormalizeUrlPaths(param.CorsUrls)
param.CorsDirs = NormalizeFsPaths(param.CorsDirs)

// auth/upload/mkdir/delete urls/dirs users
// auth/index/upload/mkdir/delete urls/dirs users
param.AuthUrlsUsers, es = normalizeAllPathValues(param.AuthUrlsUsers, true, util.NormalizeUrlPath, nil)
if len(es) == 0 {
dedupAllPathValues(param.AuthUrlsUsers)
Expand All @@ -172,6 +179,19 @@ func (param *Param) normalize() (errs []error) {
errs = append(errs, es...)
}

param.IndexUrlsUsers, es = normalizeAllPathValues(param.IndexUrlsUsers, false, util.NormalizeUrlPath, nil)
if len(es) == 0 {
dedupAllPathValues(param.IndexUrlsUsers)
} else {
errs = append(errs, es...)
}
param.IndexDirsUsers, es = normalizeAllPathValues(param.IndexDirsUsers, false, filepath.Abs, nil)
if len(es) == 0 {
dedupAllPathValues(param.IndexDirsUsers)
} else {
errs = append(errs, es...)
}

param.UploadUrlsUsers, es = normalizeAllPathValues(param.UploadUrlsUsers, false, util.NormalizeUrlPath, nil)
if len(es) == 0 {
dedupAllPathValues(param.UploadUrlsUsers)
Expand Down
12 changes: 12 additions & 0 deletions src/serverHandler/aliasHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ type aliasHandler struct {
authDirs []string
authDirsUsers pathIntsList

globalIndex bool
indexUrls []string
indexUrlsUsers pathIntsList
indexDirs []string
indexDirsUsers pathIntsList

globalUpload bool
uploadUrls []string
uploadUrlsUsers pathIntsList
Expand Down Expand Up @@ -219,6 +225,12 @@ func newAliasHandler(
authDirs: filterSuccessor(p.AuthDirs, util.HasFsPrefixDir, currentAlias.fs),
authDirsUsers: vhostCtx.authDirsUsers.filterSuccessor(true, util.HasFsPrefixDir, currentAlias.fs),

globalIndex: prefixMatched(p.IndexUrls, util.HasUrlPrefixDir, currentAlias.url) || prefixMatched(p.IndexDirs, util.HasFsPrefixDir, currentAlias.fs),
indexUrls: filterSuccessor(p.IndexUrls, util.HasUrlPrefixDir, currentAlias.url),
indexUrlsUsers: vhostCtx.indexUrlsUsers.filterSuccessor(true, util.HasUrlPrefixDir, currentAlias.url),
indexDirs: filterSuccessor(p.IndexDirs, util.HasFsPrefixDir, currentAlias.fs),
indexDirsUsers: vhostCtx.indexDirsUsers.filterSuccessor(true, util.HasFsPrefixDir, currentAlias.fs),

globalUpload: p.GlobalUpload || prefixMatched(p.UploadUrls, util.HasUrlPrefixDir, currentAlias.url) || prefixMatched(p.UploadDirs, util.HasFsPrefixDir, currentAlias.fs),
uploadUrls: filterSuccessor(p.UploadUrls, util.HasUrlPrefixDir, currentAlias.url),
uploadUrlsUsers: vhostCtx.uploadUrlsUsers.filterSuccessor(true, util.HasUrlPrefixDir, currentAlias.url),
Expand Down
12 changes: 6 additions & 6 deletions src/serverHandler/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ func (h *aliasHandler) visitTreeNode(
childSelections []string,
archiveCallback archiveCallback,
) {
if needAuth, _ := h.needAuth("", urlPath, fsPath); needAuth {
if _, _, err := h.verifyAuth(r, needAuth, urlPath, fsPath); err != nil {
return
}
needAuth, _ := h.needAuth("", urlPath, fsPath)
userId, _, err := h.verifyAuth(r, needAuth, urlPath, fsPath)
if needAuth && err != nil {
return
}

var fInfo os.FileInfo
var childInfos []os.FileInfo
// wrap func to run defer ASAP
err := func() error {
err = func() error {
var f *os.File
var err error
if statNode {
Expand Down Expand Up @@ -104,7 +104,7 @@ func (h *aliasHandler) visitTreeNode(
return
}

if fInfo.IsDir() {
if fInfo.IsDir() && h.getCanIndex(urlPath, fsPath, userId) {
childInfos, _, _ := h.mergeAlias(urlPath, fInfo, childInfos, true)
childInfos = h.FilterItems(childInfos)

Expand Down
9 changes: 4 additions & 5 deletions src/serverHandler/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,18 @@ func (h *aliasHandler) notifyAuth(w http.ResponseWriter) {
w.Header().Set("WWW-Authenticate", "Basic realm=\"files\"")
}

func (h *aliasHandler) verifyAuth(r *http.Request, needAuth bool, vhostReqPath, reqFsPath string) (userid int, username string, err error) {
user, pass, hasAuthReq := r.BasicAuth()
func (h *aliasHandler) verifyAuth(r *http.Request, needAuth bool, vhostReqPath, reqFsPath string) (authUserId int, authUserName string, err error) {
inputUser, inputPass, hasAuthReq := r.BasicAuth()

if hasAuthReq {
var success bool
userid, username, success = h.users.Auth(user, pass)
userid, username, success := h.users.Auth(inputUser, inputPass)
if success && userid >= 0 && (len(h.authUrlsUsers) > 0 || len(h.authDirsUsers) > 0) {
if matchPrefix, match := hasUrlOrDirPrefixUsers(h.authUrlsUsers, vhostReqPath, h.authDirsUsers, reqFsPath, userid); matchPrefix {
success = match
}
}
if success {
return
return userid, username, nil
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/serverHandler/perm.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ func hasUrlOrDirPrefixUsers(urlsUsers pathIntsList, reqUrl string, dirsUsers pat
return
}

func (h *aliasHandler) getCanIndex(rawReqPath, reqFsPath string, userId int) bool {
if h.globalIndex {
return true
}

if hasUrlOrDirPrefix(h.indexUrls, rawReqPath, h.indexDirs, reqFsPath) {
return true
}

if userId >= 0 {
if _, match := hasUrlOrDirPrefixUsers(h.indexUrlsUsers, rawReqPath, h.indexDirsUsers, reqFsPath, userId); match {
return true
}
}

return false
}

func (h *aliasHandler) getCanUpload(info os.FileInfo, rawReqPath, reqFsPath string, userId int) bool {
if info == nil || !info.IsDir() {
return false
Expand Down
11 changes: 8 additions & 3 deletions src/serverHandler/sessionData.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type responseData struct {
IsDelete bool
IsMutate bool

CanIndex bool
CanUpload bool
CanMkdir bool
CanDelete bool
Expand Down Expand Up @@ -394,7 +395,8 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
}
}

indexFile, indexItem, _statIdxErr := h.statIndexFile(vhostReqPath, fsPath, item, authSuccess && redirectAction == noRedirect)
canIndex := authSuccess && h.getCanIndex(vhostReqPath, fsPath, authUserId)
indexFile, indexItem, _statIdxErr := h.statIndexFile(vhostReqPath, fsPath, item, canIndex && redirectAction == noRedirect)
if _statIdxErr != nil {
errs = append(errs, _statIdxErr)
status = getStatusByErr(_statIdxErr)
Expand All @@ -417,15 +419,17 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
status = http.StatusForbidden
}

canIndex = canIndex && allowAccess

itemName := getItemName(item, r)

subItems, _readdirErr := readdir(file, item, allowAccess && authSuccess && !isMutate && redirectAction == noRedirect && NeedResponseBody(r.Method))
subItems, _readdirErr := readdir(file, item, canIndex && !isMutate && redirectAction == noRedirect && NeedResponseBody(r.Method))
if _readdirErr != nil {
errs = append(errs, _readdirErr)
status = http.StatusInternalServerError
}

subItems, aliasSubItems, _mergeErrs := h.mergeAlias(vhostReqPath, item, subItems, allowAccess && authSuccess && redirectAction == noRedirect)
subItems, aliasSubItems, _mergeErrs := h.mergeAlias(vhostReqPath, item, subItems, canIndex && redirectAction == noRedirect)
if len(_mergeErrs) > 0 {
errs = append(errs, _mergeErrs...)
status = http.StatusInternalServerError
Expand Down Expand Up @@ -497,6 +501,7 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
IsDelete: isDelete,
IsMutate: isMutate,

CanIndex: canIndex,
CanUpload: canUpload,
CanMkdir: canMkdir,
CanDelete: canDelete,
Expand Down
8 changes: 7 additions & 1 deletion src/serverHandler/vhostHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type vhostContext struct {

authUrlsUsers pathIntsList
authDirsUsers pathIntsList
indexUrlsUsers pathIntsList
indexDirsUsers pathIntsList
uploadUrlsUsers pathIntsList
uploadDirsUsers pathIntsList
mkdirUrlsUsers pathIntsList
Expand Down Expand Up @@ -84,9 +86,11 @@ func NewVhostHandler(
return nil, errs
}

// auth/upload/mkdir/delete urls/dirs users
// auth/index/upload/mkdir/delete urls/dirs users
authUrlsUsers := pathUsernamesToPathUids(users, p.AuthUrlsUsers)
authDirsUsers := pathUsernamesToPathUids(users, p.AuthDirsUsers)
indexUrlsUsers := pathUsernamesToPathUids(users, p.IndexUrlsUsers)
indexDirsUsers := pathUsernamesToPathUids(users, p.IndexDirsUsers)
uploadUrlsUsers := pathUsernamesToPathUids(users, p.UploadUrlsUsers)
uploadDirsUsers := pathUsernamesToPathUids(users, p.UploadDirsUsers)
mkdirUrlsUsers := pathUsernamesToPathUids(users, p.MkdirUrlsUsers)
Expand All @@ -109,6 +113,8 @@ func NewVhostHandler(
users: users,
authUrlsUsers: authUrlsUsers,
authDirsUsers: authDirsUsers,
indexUrlsUsers: indexUrlsUsers,
indexDirsUsers: indexDirsUsers,
uploadUrlsUsers: uploadUrlsUsers,
uploadDirsUsers: uploadDirsUsers,
mkdirUrlsUsers: mkdirUrlsUsers,
Expand Down
46 changes: 46 additions & 0 deletions test/case/042.index.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash

source "$root"/lib.bash

"$ghfs" -l 3003 -r "$fs"/vhost1 -a :/x/y/z:"$fs"/vhost2 --user foo:123 bar:456 --index /hello --index-dir "$fs"/vhost1/world --index-user :/x/y/z/a:foo --index-dir-user :"$fs"/vhost2/b:bar -E '' &
sleep 0.05 # wait server ready

# --index

cnt=$(curl_get_body 'http://127.0.0.1:3003/?json' | jq '.subItems | length')
assert "$cnt" '0'

cnt=$(curl_get_body 'http://127.0.0.1:3003/go/?json' | jq '.subItems | length')
assert "$cnt" '0'

cnt=$(curl_get_body 'http://127.0.0.1:3003/hello/?json' | jq '.subItems | length')
[ "$cnt" == "0" ] && fail "subItems should not be 0"

# --index-dir

cnt=$(curl_get_body 'http://127.0.0.1:3003/world/?json' | jq '.subItems | length')
[ "$cnt" == "0" ] && fail "subItems should not be 0"

# --index-user

cnt=$(curl_get_body 'http://127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length')
assert "$cnt" '0'

cnt=$(curl_get_body 'http://baz:789@127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length')
assert "$cnt" '0'

cnt=$(curl_get_body 'http://foo:123@127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length')
[ "$cnt" == "0" ] && fail "subItems should not be 0"

# --index-dir-user

cnt=$(curl_get_body 'http://127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length')
assert "$cnt" '0'

cnt=$(curl_get_body 'http://baz:789@127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length')
assert "$cnt" '0'

cnt=$(curl_get_body 'http://bar:456@127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length')
[ "$cnt" == "0" ] && fail "subItems should not be 0"

jobs -p | xargs kill &> /dev/null

0 comments on commit faf4fb0

Please sign in to comment.