Skip to content

Commit faf4fb0

Browse files
committed
feat: add option to control directory index
- `--index` - `--index-user` - `--index-dir` - `--index-dir-user`
1 parent d7ca8db commit faf4fb0

File tree

11 files changed

+163
-18
lines changed

11 files changed

+163
-18
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,15 @@ ghfs [options]
190190
--auth-dir-user <separator><fs-path>[<separator><allowed-username>...] ...
191191
Use Basic Auth for specific file system paths(and sub paths).
192192
193+
--index <url-path> ...
194+
--index-user <separator><url-path>[<separator><allowed-username>...] ...
195+
Set url paths(and sub paths) that allows to index files of a directory.
196+
--index defaults to "/".
197+
Set to "" to disable index.
198+
--index-dir <fs-path> ...
199+
--index-dir-user <separator><fs-path>[<separator><allowed-username>...] ...
200+
Similar to --index, but use file system path instead of url path.
201+
193202
-U|--global-upload
194203
Allow upload files for all url paths.
195204
Use it with care.

README.zh-CN.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ ghfs [选项]
181181
--auth-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ...
182182
对指定文件系统路径(及子路径)启用http基本验证。
183183
184+
--index <URL路径> ...
185+
--index-user <分隔符><URL路径>[<分隔符><允许的用户名>...] ...
186+
设置允许索引目录的URL路径(及子路径)。
187+
--index默认值为"/"。
188+
设为""来禁用索引。
189+
--index-dir <文件系统路径> ...
190+
--index-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ...
191+
与--index类似,但指定的是文件系统路径,而不是URL路径。
192+
184193
-U|--global-upload
185194
对所有URL路径开启上传权限。
186195
请谨慎使用。

src/param/cli.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ func NewCliCmd() *goNixArgParser.Command {
7575
err = options.AddFlagValues("authdirsusers", "--auth-dir-user", "", nil, "file system path that require Basic Auth for specific users, <sep><fs-path>[<sep><user>...]")
7676
serverError.CheckFatal(err)
7777

78+
err = options.AddFlagValues("indexurls", "--index", "", []string{"/"}, "url path that allow directory index")
79+
serverError.CheckFatal(err)
80+
81+
err = options.AddFlagValues("indexurlsusers", "--index-user", "", nil, "url path that allow index files for specific users, <sep><url-path>[<sep><user>...]")
82+
serverError.CheckFatal(err)
83+
84+
err = options.AddFlagValues("indexdirs", "--index-dir", "", nil, "file system path that allow index files")
85+
serverError.CheckFatal(err)
86+
87+
err = options.AddFlagValues("indexdirsusers", "--index-dir-user", "", nil, "file system path that allow index files for specific users, <sep><fs-path>[<sep><user>...]")
88+
serverError.CheckFatal(err)
89+
7890
err = options.AddFlags("globalupload", []string{"-U", "--global-upload"}, "", "allow upload files for all url paths")
7991
serverError.CheckFatal(err)
8092

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

351-
// auth/upload/mkdir/delete/archive/cors urls/dirs
363+
// auth/index/upload/mkdir/delete/archive/cors urls/dirs
352364
param.GlobalAuth = result.HasKey("globalauth")
353365
param.AuthUrls, _ = result.GetStrings("authurls")
354366
param.AuthDirs, _ = result.GetStrings("authdirs")
355367

368+
param.IndexUrls, _ = result.GetStrings("indexurls")
369+
param.IndexDirs, _ = result.GetStrings("indexdirs")
370+
356371
param.GlobalUpload = result.HasKey("globalupload")
357372
param.UploadUrls, _ = result.GetStrings("uploadurls")
358373
param.UploadDirs, _ = result.GetStrings("uploaddirs")
@@ -380,6 +395,12 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e
380395
authDirsUsers, _ := result.GetStrings("authdirsusers")
381396
param.AuthDirsUsers = SplitAllKeyValues(authDirsUsers)
382397

398+
indexUrlsUsers, _ := result.GetStrings("indexurlsusers")
399+
param.IndexUrlsUsers = SplitAllKeyValues(indexUrlsUsers)
400+
401+
indexDirsUsers, _ := result.GetStrings("indexdirsusers")
402+
param.IndexDirsUsers = SplitAllKeyValues(indexDirsUsers)
403+
383404
uploadUrlsUsers, _ := result.GetStrings("uploadurlsusers")
384405
param.UploadUrlsUsers = SplitAllKeyValues(uploadUrlsUsers)
385406

src/param/main.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ type Param struct {
4444
HeadersUrls [][]string
4545
HeadersDirs [][]string
4646

47+
IndexUrls []string
48+
IndexUrlsUsers [][]string // [][path, user...]
49+
IndexDirs []string
50+
IndexDirsUsers [][]string // [][path, user...]
51+
4752
GlobalUpload bool
4853
UploadUrls []string
4954
UploadUrlsUsers [][]string // [][path, user...]
@@ -144,9 +149,11 @@ func (param *Param) normalize() (errs []error) {
144149
// dir indexes
145150
param.DirIndexes = normalizeFilenames(param.DirIndexes)
146151

147-
// auth/upload/mkdir/delete/archive/cors urls/dirs
152+
// auth/index/upload/mkdir/delete/archive/cors urls/dirs
148153
param.AuthUrls = NormalizeUrlPaths(param.AuthUrls)
149154
param.AuthDirs = NormalizeFsPaths(param.AuthDirs)
155+
param.IndexUrls = NormalizeUrlPaths(param.IndexUrls)
156+
param.IndexDirs = NormalizeFsPaths(param.IndexDirs)
150157
param.UploadUrls = NormalizeUrlPaths(param.UploadUrls)
151158
param.UploadDirs = NormalizeFsPaths(param.UploadDirs)
152159
param.MkdirUrls = NormalizeUrlPaths(param.MkdirUrls)
@@ -158,7 +165,7 @@ func (param *Param) normalize() (errs []error) {
158165
param.CorsUrls = NormalizeUrlPaths(param.CorsUrls)
159166
param.CorsDirs = NormalizeFsPaths(param.CorsDirs)
160167

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

182+
param.IndexUrlsUsers, es = normalizeAllPathValues(param.IndexUrlsUsers, false, util.NormalizeUrlPath, nil)
183+
if len(es) == 0 {
184+
dedupAllPathValues(param.IndexUrlsUsers)
185+
} else {
186+
errs = append(errs, es...)
187+
}
188+
param.IndexDirsUsers, es = normalizeAllPathValues(param.IndexDirsUsers, false, filepath.Abs, nil)
189+
if len(es) == 0 {
190+
dedupAllPathValues(param.IndexDirsUsers)
191+
} else {
192+
errs = append(errs, es...)
193+
}
194+
175195
param.UploadUrlsUsers, es = normalizeAllPathValues(param.UploadUrlsUsers, false, util.NormalizeUrlPath, nil)
176196
if len(es) == 0 {
177197
dedupAllPathValues(param.UploadUrlsUsers)

src/serverHandler/aliasHandler.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ type aliasHandler struct {
4646
authDirs []string
4747
authDirsUsers pathIntsList
4848

49+
globalIndex bool
50+
indexUrls []string
51+
indexUrlsUsers pathIntsList
52+
indexDirs []string
53+
indexDirsUsers pathIntsList
54+
4955
globalUpload bool
5056
uploadUrls []string
5157
uploadUrlsUsers pathIntsList
@@ -219,6 +225,12 @@ func newAliasHandler(
219225
authDirs: filterSuccessor(p.AuthDirs, util.HasFsPrefixDir, currentAlias.fs),
220226
authDirsUsers: vhostCtx.authDirsUsers.filterSuccessor(true, util.HasFsPrefixDir, currentAlias.fs),
221227

228+
globalIndex: prefixMatched(p.IndexUrls, util.HasUrlPrefixDir, currentAlias.url) || prefixMatched(p.IndexDirs, util.HasFsPrefixDir, currentAlias.fs),
229+
indexUrls: filterSuccessor(p.IndexUrls, util.HasUrlPrefixDir, currentAlias.url),
230+
indexUrlsUsers: vhostCtx.indexUrlsUsers.filterSuccessor(true, util.HasUrlPrefixDir, currentAlias.url),
231+
indexDirs: filterSuccessor(p.IndexDirs, util.HasFsPrefixDir, currentAlias.fs),
232+
indexDirsUsers: vhostCtx.indexDirsUsers.filterSuccessor(true, util.HasFsPrefixDir, currentAlias.fs),
233+
222234
globalUpload: p.GlobalUpload || prefixMatched(p.UploadUrls, util.HasUrlPrefixDir, currentAlias.url) || prefixMatched(p.UploadDirs, util.HasFsPrefixDir, currentAlias.fs),
223235
uploadUrls: filterSuccessor(p.UploadUrls, util.HasUrlPrefixDir, currentAlias.url),
224236
uploadUrlsUsers: vhostCtx.uploadUrlsUsers.filterSuccessor(true, util.HasUrlPrefixDir, currentAlias.url),

src/serverHandler/archive.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,16 @@ func (h *aliasHandler) visitTreeNode(
5252
childSelections []string,
5353
archiveCallback archiveCallback,
5454
) {
55-
if needAuth, _ := h.needAuth("", urlPath, fsPath); needAuth {
56-
if _, _, err := h.verifyAuth(r, needAuth, urlPath, fsPath); err != nil {
57-
return
58-
}
55+
needAuth, _ := h.needAuth("", urlPath, fsPath)
56+
userId, _, err := h.verifyAuth(r, needAuth, urlPath, fsPath)
57+
if needAuth && err != nil {
58+
return
5959
}
6060

6161
var fInfo os.FileInfo
6262
var childInfos []os.FileInfo
6363
// wrap func to run defer ASAP
64-
err := func() error {
64+
err = func() error {
6565
var f *os.File
6666
var err error
6767
if statNode {
@@ -104,7 +104,7 @@ func (h *aliasHandler) visitTreeNode(
104104
return
105105
}
106106

107-
if fInfo.IsDir() {
107+
if fInfo.IsDir() && h.getCanIndex(urlPath, fsPath, userId) {
108108
childInfos, _, _ := h.mergeAlias(urlPath, fInfo, childInfos, true)
109109
childInfos = h.FilterItems(childInfos)
110110

src/serverHandler/auth.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,18 @@ func (h *aliasHandler) notifyAuth(w http.ResponseWriter) {
3333
w.Header().Set("WWW-Authenticate", "Basic realm=\"files\"")
3434
}
3535

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

3939
if hasAuthReq {
40-
var success bool
41-
userid, username, success = h.users.Auth(user, pass)
40+
userid, username, success := h.users.Auth(inputUser, inputPass)
4241
if success && userid >= 0 && (len(h.authUrlsUsers) > 0 || len(h.authDirsUsers) > 0) {
4342
if matchPrefix, match := hasUrlOrDirPrefixUsers(h.authUrlsUsers, vhostReqPath, h.authDirsUsers, reqFsPath, userid); matchPrefix {
4443
success = match
4544
}
4645
}
4746
if success {
48-
return
47+
return userid, username, nil
4948
}
5049
}
5150

src/serverHandler/perm.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,24 @@ func hasUrlOrDirPrefixUsers(urlsUsers pathIntsList, reqUrl string, dirsUsers pat
5757
return
5858
}
5959

60+
func (h *aliasHandler) getCanIndex(rawReqPath, reqFsPath string, userId int) bool {
61+
if h.globalIndex {
62+
return true
63+
}
64+
65+
if hasUrlOrDirPrefix(h.indexUrls, rawReqPath, h.indexDirs, reqFsPath) {
66+
return true
67+
}
68+
69+
if userId >= 0 {
70+
if _, match := hasUrlOrDirPrefixUsers(h.indexUrlsUsers, rawReqPath, h.indexDirsUsers, reqFsPath, userId); match {
71+
return true
72+
}
73+
}
74+
75+
return false
76+
}
77+
6078
func (h *aliasHandler) getCanUpload(info os.FileInfo, rawReqPath, reqFsPath string, userId int) bool {
6179
if info == nil || !info.IsDir() {
6280
return false

src/serverHandler/sessionData.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ type responseData struct {
6666
IsDelete bool
6767
IsMutate bool
6868

69+
CanIndex bool
6970
CanUpload bool
7071
CanMkdir bool
7172
CanDelete bool
@@ -394,7 +395,8 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
394395
}
395396
}
396397

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

422+
canIndex = canIndex && allowAccess
423+
420424
itemName := getItemName(item, r)
421425

422-
subItems, _readdirErr := readdir(file, item, allowAccess && authSuccess && !isMutate && redirectAction == noRedirect && NeedResponseBody(r.Method))
426+
subItems, _readdirErr := readdir(file, item, canIndex && !isMutate && redirectAction == noRedirect && NeedResponseBody(r.Method))
423427
if _readdirErr != nil {
424428
errs = append(errs, _readdirErr)
425429
status = http.StatusInternalServerError
426430
}
427431

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

504+
CanIndex: canIndex,
500505
CanUpload: canUpload,
501506
CanMkdir: canMkdir,
502507
CanDelete: canDelete,

src/serverHandler/vhostHandler.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type vhostContext struct {
2424

2525
authUrlsUsers pathIntsList
2626
authDirsUsers pathIntsList
27+
indexUrlsUsers pathIntsList
28+
indexDirsUsers pathIntsList
2729
uploadUrlsUsers pathIntsList
2830
uploadDirsUsers pathIntsList
2931
mkdirUrlsUsers pathIntsList
@@ -84,9 +86,11 @@ func NewVhostHandler(
8486
return nil, errs
8587
}
8688

87-
// auth/upload/mkdir/delete urls/dirs users
89+
// auth/index/upload/mkdir/delete urls/dirs users
8890
authUrlsUsers := pathUsernamesToPathUids(users, p.AuthUrlsUsers)
8991
authDirsUsers := pathUsernamesToPathUids(users, p.AuthDirsUsers)
92+
indexUrlsUsers := pathUsernamesToPathUids(users, p.IndexUrlsUsers)
93+
indexDirsUsers := pathUsernamesToPathUids(users, p.IndexDirsUsers)
9094
uploadUrlsUsers := pathUsernamesToPathUids(users, p.UploadUrlsUsers)
9195
uploadDirsUsers := pathUsernamesToPathUids(users, p.UploadDirsUsers)
9296
mkdirUrlsUsers := pathUsernamesToPathUids(users, p.MkdirUrlsUsers)
@@ -109,6 +113,8 @@ func NewVhostHandler(
109113
users: users,
110114
authUrlsUsers: authUrlsUsers,
111115
authDirsUsers: authDirsUsers,
116+
indexUrlsUsers: indexUrlsUsers,
117+
indexDirsUsers: indexDirsUsers,
112118
uploadUrlsUsers: uploadUrlsUsers,
113119
uploadDirsUsers: uploadDirsUsers,
114120
mkdirUrlsUsers: mkdirUrlsUsers,

test/case/042.index.bash

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
3+
source "$root"/lib.bash
4+
5+
"$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 '' &
6+
sleep 0.05 # wait server ready
7+
8+
# --index
9+
10+
cnt=$(curl_get_body 'http://127.0.0.1:3003/?json' | jq '.subItems | length')
11+
assert "$cnt" '0'
12+
13+
cnt=$(curl_get_body 'http://127.0.0.1:3003/go/?json' | jq '.subItems | length')
14+
assert "$cnt" '0'
15+
16+
cnt=$(curl_get_body 'http://127.0.0.1:3003/hello/?json' | jq '.subItems | length')
17+
[ "$cnt" == "0" ] && fail "subItems should not be 0"
18+
19+
# --index-dir
20+
21+
cnt=$(curl_get_body 'http://127.0.0.1:3003/world/?json' | jq '.subItems | length')
22+
[ "$cnt" == "0" ] && fail "subItems should not be 0"
23+
24+
# --index-user
25+
26+
cnt=$(curl_get_body 'http://127.0.0.1:3003/x/y/z/a/?json' | jq '.subItems | length')
27+
assert "$cnt" '0'
28+
29+
cnt=$(curl_get_body 'http://baz:[email protected]:3003/x/y/z/a/?json' | jq '.subItems | length')
30+
assert "$cnt" '0'
31+
32+
cnt=$(curl_get_body 'http://foo:[email protected]:3003/x/y/z/a/?json' | jq '.subItems | length')
33+
[ "$cnt" == "0" ] && fail "subItems should not be 0"
34+
35+
# --index-dir-user
36+
37+
cnt=$(curl_get_body 'http://127.0.0.1:3003/x/y/z/b/?json' | jq '.subItems | length')
38+
assert "$cnt" '0'
39+
40+
cnt=$(curl_get_body 'http://baz:[email protected]:3003/x/y/z/b/?json' | jq '.subItems | length')
41+
assert "$cnt" '0'
42+
43+
cnt=$(curl_get_body 'http://bar:[email protected]:3003/x/y/z/b/?json' | jq '.subItems | length')
44+
[ "$cnt" == "0" ] && fail "subItems should not be 0"
45+
46+
jobs -p | xargs kill &> /dev/null

0 commit comments

Comments
 (0)