Skip to content

Commit 79e13f1

Browse files
committed
feat: add http Basic Auth options
1 parent a765f5a commit 79e13f1

File tree

8 files changed

+149
-2
lines changed

8 files changed

+149
-2
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ server [options]
7878
--cors-dir <fs-path> ...
7979
Allow CORS requests for specific file system path.
8080
81+
--global-auth
82+
Use Basic Auth for all url path.
83+
--auth <url-path> ...
84+
Use Basic Auth for specific url path.
85+
--auth-dir <fs-path> ...
86+
Use Basic Auth for specific file system path.
87+
--user [<username>]:[<password>] ...
88+
Specify users for Basic Auth, empty username and/or password is allowed.
89+
8190
-c|--cert <file>
8291
Specify TLS certificate file.
8392

src/param/cli.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package param
33
import (
44
"../goNixArgParser"
55
"../serverErrHandler"
6+
"errors"
67
"io/ioutil"
78
"os"
89
"strings"
@@ -50,6 +51,18 @@ func init() {
5051
err = options.AddFlagValues("corsdirs", "--cors-dir", "", nil, "file system path that enable CORS headers")
5152
serverErrHandler.CheckFatal(err)
5253

54+
err = options.AddFlag("globalauth", "--global-auth", "GHFS_GLOBAL_AUTH", "require Basic Auth for all directories")
55+
serverErrHandler.CheckFatal(err)
56+
57+
err = options.AddFlagValues("authurls", "--auth", "", nil, "url path that require Basic Auth")
58+
serverErrHandler.CheckFatal(err)
59+
60+
err = options.AddFlagValues("authdirs", "--auth-dir", "", nil, "file system path that require Basic Auth")
61+
serverErrHandler.CheckFatal(err)
62+
63+
err = options.AddFlagValues("users", "--user", "", nil, "user info: <username>:<password>")
64+
serverErrHandler.CheckFatal(err)
65+
5366
err = options.AddFlagsValue("key", []string{"-k", "--key"}, "GHFS_KEY", "", "TLS certificate key path")
5467
serverErrHandler.CheckFatal(err)
5568

@@ -152,6 +165,7 @@ func doParseCli() []*Param {
152165
param.GlobalUpload = result.HasKey("globalupload")
153166
param.GlobalArchive = result.HasKey("globalarchive")
154167
param.GlobalCors = result.HasKey("globalcors")
168+
param.GlobalAuth = result.HasKey("globalauth")
155169
param.Key, _ = result.GetString("key")
156170
param.Cert, _ = result.GetString("cert")
157171
param.Hostnames, _ = result.GetStrings("hostnames")
@@ -198,6 +212,34 @@ func doParseCli() []*Param {
198212
arrCorsDirs, _ := result.GetStrings("corsdirs")
199213
param.CorsDirs = normalizeFsPaths(arrCorsDirs)
200214

215+
// normalize auth urls
216+
arrAuthUrls, _ := result.GetStrings("authurls")
217+
param.AuthUrls = normalizeUrlPaths(arrAuthUrls)
218+
219+
// normalize auth dirs
220+
arrAuthDirs, _ := result.GetStrings("authdirs")
221+
param.AuthDirs = normalizeFsPaths(arrAuthDirs)
222+
223+
// normalize users
224+
param.Users = map[string]string{}
225+
arrUsers, _ := result.GetStrings("users")
226+
for _, userEntry := range arrUsers {
227+
username := userEntry
228+
password := ""
229+
230+
colonIndex := strings.IndexByte(userEntry, ':')
231+
if colonIndex >= 0 {
232+
username = userEntry[:colonIndex]
233+
password = userEntry[colonIndex+1:]
234+
}
235+
236+
if _, ok := param.Users[username]; ok {
237+
serverErrHandler.CheckError(errors.New("Duplicated username: " + username))
238+
} else {
239+
param.Users[username] = password
240+
}
241+
}
242+
201243
// shows
202244
shows, err := getWildcardRegexp(result.GetStrings("shows"))
203245
serverErrHandler.CheckFatal(err)

src/param/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ type Param struct {
2525
CorsUrls []string
2626
CorsDirs []string
2727

28+
GlobalAuth bool
29+
AuthUrls []string
30+
AuthDirs []string
31+
Users map[string]string
32+
2833
Key string
2934
Cert string
3035
Listen []string

src/serveMux/main.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"../serverHandler"
77
"../serverLog"
88
"../tpl"
9+
"../user"
910
"net/http"
1011
)
1112

@@ -20,18 +21,23 @@ func NewServeMux(
2021
logger *serverLog.Logger,
2122
errorHandler *serverErrHandler.ErrHandler,
2223
) *ServeMux {
24+
users := user.NewUsers()
25+
for username, password := range p.Users {
26+
users.Add(username, password)
27+
}
28+
2329
tplObj, err := tpl.LoadPage(p.Template)
2430
errorHandler.LogError(err)
2531

2632
aliases := p.Aliases
2733
handlers := map[string]http.Handler{}
2834

2935
if _, hasRootAlias := aliases["/"]; !hasRootAlias {
30-
handlers["/"] = serverHandler.NewHandler(p.Root, "/", p, tplObj, logger, errorHandler)
36+
handlers["/"] = serverHandler.NewHandler(p.Root, "/", p, users, tplObj, logger, errorHandler)
3137
}
3238

3339
for urlPath, fsPath := range p.Aliases {
34-
handlers[urlPath] = serverHandler.NewHandler(fsPath, urlPath, p, tplObj, logger, errorHandler)
40+
handlers[urlPath] = serverHandler.NewHandler(fsPath, urlPath, p, users, tplObj, logger, errorHandler)
3541
}
3642

3743
// create ServeMux

src/serverHandler/auth.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package serverHandler
2+
3+
import "net/http"
4+
5+
func (h *handler) auth(w http.ResponseWriter, r *http.Request) (success bool) {
6+
header := w.Header()
7+
header.Set("WWW-Authenticate", "Basic realm=\""+r.URL.Path+"\"")
8+
9+
username, password, hasAuthReq := r.BasicAuth()
10+
if (hasAuthReq) {
11+
success = h.users.Auth(username, password)
12+
}
13+
14+
if !success {
15+
w.WriteHeader(http.StatusUnauthorized)
16+
}
17+
18+
return
19+
}

src/serverHandler/main.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"../param"
55
"../serverErrHandler"
66
"../serverLog"
7+
"../user"
78
"html/template"
89
"net/http"
910
"regexp"
@@ -26,6 +27,11 @@ type handler struct {
2627
corsUrls []string
2728
corsDirs []string
2829

30+
globalAuth bool
31+
authUrls []string
32+
authDirs []string
33+
users user.Users
34+
2935
shows *regexp.Regexp
3036
showDirs *regexp.Regexp
3137
showFiles *regexp.Regexp
@@ -66,6 +72,10 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6672
}()
6773
}
6874

75+
if data.NeedAuth && !h.auth(w, r) {
76+
return
77+
}
78+
6979
if data.CanUpload && r.Method == "POST" {
7080
h.saveUploadFiles(data.handlerReqPath, r)
7181
http.Redirect(w, r, r.RequestURI, http.StatusFound)
@@ -102,6 +112,7 @@ func NewHandler(
102112
root string,
103113
urlPrefix string,
104114
p *param.Param,
115+
users user.Users,
105116
template *template.Template,
106117
logger *serverLog.Logger,
107118
errHandler *serverErrHandler.ErrHandler,
@@ -123,6 +134,11 @@ func NewHandler(
123134
corsUrls: p.CorsUrls,
124135
corsDirs: p.CorsDirs,
125136

137+
globalAuth: p.GlobalAuth,
138+
authUrls: p.AuthUrls,
139+
authDirs: p.AuthDirs,
140+
users: users,
141+
126142
shows: p.Shows,
127143
showDirs: p.ShowDirs,
128144
showFiles: p.ShowFiles,

src/serverHandler/responseData.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type responseData struct {
3434
CanUpload bool
3535
CanArchive bool
3636
CanCors bool
37+
NeedAuth bool
3738
Errors []error
3839
}
3940

@@ -297,6 +298,14 @@ func (h *handler) getCanCors(rawReqPath, reqFsPath string) bool {
297298
return hasUrlOrDirPrefix(h.corsUrls, rawReqPath, h.corsDirs, reqFsPath)
298299
}
299300

301+
func (h *handler) getNeedAuth(rawReqPath, reqFsPath string) bool {
302+
if h.globalAuth {
303+
return true
304+
}
305+
306+
return hasUrlOrDirPrefix(h.authUrls, rawReqPath, h.authDirs, reqFsPath)
307+
}
308+
300309
func (h *handler) getResponseData(r *http.Request) (data *responseData) {
301310
requestUri := r.URL.Path
302311
tailSlash := requestUri[len(requestUri)-1] == '/'
@@ -347,6 +356,7 @@ func (h *handler) getResponseData(r *http.Request) (data *responseData) {
347356
canUpload := h.getCanUpload(item, rawReqPath, reqFsPath)
348357
canArchive := h.getCanArchive(subItems, rawReqPath, reqFsPath)
349358
canCors := h.getCanCors(rawReqPath, reqFsPath)
359+
needAuth := h.getNeedAuth(rawReqPath, reqFsPath)
350360

351361
data = &responseData{
352362
rawReqPath: rawReqPath,
@@ -368,6 +378,7 @@ func (h *handler) getResponseData(r *http.Request) (data *responseData) {
368378
CanUpload: canUpload,
369379
CanArchive: canArchive,
370380
CanCors: canCors,
381+
NeedAuth: needAuth,
371382

372383
Errors: errs,
373384
}

src/user/main.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package user
2+
3+
import "errors"
4+
5+
type user struct {
6+
username string
7+
password string
8+
}
9+
10+
func newUser(username, password string) *user {
11+
return &user{
12+
username,
13+
password,
14+
}
15+
}
16+
17+
type Users struct {
18+
pool map[string]*user
19+
}
20+
21+
func (users Users) Add(username, password string) error {
22+
if _, exist := users.pool[username]; exist {
23+
return errors.New("username already exist")
24+
}
25+
26+
users.pool[username] = newUser(username, password)
27+
return nil
28+
}
29+
30+
func (users Users) Auth(username, password string) bool {
31+
user, exist := users.pool[username]
32+
return exist && user.password == password
33+
}
34+
35+
func NewUsers() Users {
36+
return Users{
37+
pool: map[string]*user{},
38+
}
39+
}

0 commit comments

Comments
 (0)