From 5e8c4d4d4573459a8621375934604d2e7f0f28a1 Mon Sep 17 00:00:00 2001 From: Marvin Weck Date: Tue, 31 Jul 2018 17:51:44 +0200 Subject: [PATCH] Fix #471 --- cmd/filebrowser/main.go | 27 ++++++++++++++++--- doc.go | 4 +++ filebrowser.go | 8 ++++++ http/auth.go | 57 ++++++++++++++++++++++++++--------------- 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/cmd/filebrowser/main.go b/cmd/filebrowser/main.go index b8b32272..5ef469db 100644 --- a/cmd/filebrowser/main.go +++ b/cmd/filebrowser/main.go @@ -9,11 +9,8 @@ import ( "os" "path/filepath" "strings" - "github.com/asdine/storm" - "gopkg.in/natefinch/lumberjack.v2" - "github.com/filebrowser/filebrowser" "github.com/filebrowser/filebrowser/bolt" h "github.com/filebrowser/filebrowser/http" @@ -38,6 +35,10 @@ var ( recaptchakey string recaptchasecret string port int + auth struct { + method string + loginHeader string + } noAuth bool allowCommands bool allowEdit bool @@ -63,6 +64,8 @@ func init() { flag.BoolVar(&allowCommands, "allow-commands", true, "Default allow commands option for new users") flag.BoolVar(&allowEdit, "allow-edit", true, "Default allow edit option for new users") flag.BoolVar(&allowPublish, "allow-publish", true, "Default allow publish option for new users") + flag.StringVar(&auth.method, "auth.method", "default", "Switch between 'default' and 'proxy' authentication.") + flag.StringVar(&auth.loginHeader, "auth.login-header", "X-Forwarded-User", "The header name used for proxy authentication.") flag.BoolVar(&allowNew, "allow-new", true, "Default allow new option for new users") flag.BoolVar(&noAuth, "no-auth", false, "Disables authentication") flag.BoolVar(&alterRecaptcha, "alternative-recaptcha", false, "Use recaptcha.net for serving and handling, useful in China") @@ -84,6 +87,8 @@ func setupViper() { viper.SetDefault("AllowPublish", true) viper.SetDefault("StaticGen", "") viper.SetDefault("Locale", "") + viper.SetDefault("AuthMethod", "default") + viper.SetDefault("LoginHeader", "X-Fowarded-User"); viper.SetDefault("NoAuth", false) viper.SetDefault("BaseURL", "") viper.SetDefault("PrefixURL", "") @@ -104,6 +109,8 @@ func setupViper() { viper.BindPFlag("AllowPublish", flag.Lookup("allow-publish")) viper.BindPFlag("Locale", flag.Lookup("locale")) viper.BindPFlag("StaticGen", flag.Lookup("staticgen")) + viper.BindPFlag("AuthMethod", flag.Lookup("auth.method")) + viper.BindPFlag("LoginHeader", flag.Lookup("auth.login-header")) viper.BindPFlag("NoAuth", flag.Lookup("no-auth")) viper.BindPFlag("BaseURL", flag.Lookup("baseurl")) viper.BindPFlag("PrefixURL", flag.Lookup("prefixurl")) @@ -168,6 +175,18 @@ func main() { }) } + // Validate the provided config before moving forward + if(viper.GetString("AuthMethod") != "default" && viper.GetString("AuthMethod") != "proxy") { + log.Fatal("The property 'auth.method' needs to be set to 'default' or 'proxy'.") + } + + if (viper.GetString("AuthMethod") == "proxy") { + if(viper.GetString("LoginHeader") == "") { + log.Fatal("The 'login-header' needs to be specified when 'proxy' authentication is used.") + } + log.Println("[WARN] Filebrowser authentication is configured to 'proxy' authentication. This can cause a huge security issue if the infrastructure is not configured correctly.") + } + // Builds the address and a listener. laddr := viper.GetString("Address") + ":" + viper.GetString("Port") listener, err := net.Listen("tcp", laddr) @@ -196,6 +215,8 @@ func handler() http.Handler { } fm := &filebrowser.FileBrowser{ + AuthMethod: viper.GetString("AuthMethod"), + LoginHeader: viper.GetString("LoginHeader"), NoAuth: viper.GetBool("NoAuth"), BaseURL: viper.GetString("BaseURL"), PrefixURL: viper.GetString("PrefixURL"), diff --git a/doc.go b/doc.go index 0930a602..82fe9895 100644 --- a/doc.go +++ b/doc.go @@ -16,6 +16,10 @@ to import "github.com/filebrowser/filebrowser/bolt". m := &fm.FileBrowser{ NoAuth: false, + Auth: { + Type: "default", + LoginHeader: "X-Fowarded-User" + }, DefaultUser: &fm.User{ AllowCommands: true, AllowEdit: true, diff --git a/filebrowser.go b/filebrowser.go index 1b23e24a..baa102b8 100644 --- a/filebrowser.go +++ b/filebrowser.go @@ -71,6 +71,14 @@ type FileBrowser struct { // there will only exist one user, called "admin". NoAuth bool + // Define if either, the common authentication mechansim or 'proxy' authentication should be used. + // 'proxy' authentication enables a mechanism that authenticates a user based on forwarded + // headers. + AuthMethod string + + // When 'AuthMethod' is set to 'proxy' the header configured below is used to identify the user. + LoginHeader string + // ReCaptcha host, key and secret. ReCaptchaHost string ReCaptchaKey string diff --git a/http/auth.go b/http/auth.go index 341e299c..155fcab0 100644 --- a/http/auth.go +++ b/http/auth.go @@ -51,31 +51,33 @@ func reCaptcha(host, secret, response string) (bool, error) { // authHandler processes the authentication for the user. func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, error) { - // NoAuth instances shouldn't call this method. if c.NoAuth { + // NoAuth instances shouldn't call this method. return 0, nil - } + } else if c.AuthMethod == "proxy" { + // Receive the Username from the Header. + cred.Username = r.Header.Get(c.LoginHeader) + } else { + // Receive the credentials from the request and unmarshal them. + if r.Body == nil { + return http.StatusForbidden, nil + } - // Receive the credentials from the request and unmarshal them. - var cred cred - if r.Body == nil { - return http.StatusForbidden, nil - } - - err := json.NewDecoder(r.Body).Decode(&cred) - if err != nil { - return http.StatusForbidden, nil - } - - // If ReCaptcha is enabled, check the code. - if len(c.ReCaptchaSecret) > 0 { - ok, err := reCaptcha(c.ReCaptchaHost, c.ReCaptchaSecret, cred.ReCaptcha) + err := json.NewDecoder(r.Body).Decode(&cred) if err != nil { return http.StatusForbidden, err } - if !ok { - return http.StatusForbidden, nil + // If ReCaptcha is enabled, check the code. + if len(c.ReCaptchaSecret) > 0 { + ok, err := reCaptcha(c.ReCaptchaHost, c.ReCaptchaSecret, cred.ReCaptcha) + if err != nil { + return http.StatusForbidden, err + } + + if !ok { + return http.StatusForbidden, nil + } } } @@ -85,9 +87,11 @@ func authHandler(c *fb.Context, w http.ResponseWriter, r *http.Request) (int, er return http.StatusForbidden, nil } - // Checks if the password is correct. - if !fb.CheckPasswordHash(cred.Password, u.Password) { - return http.StatusForbidden, nil + if c.AuthMethod != "proxy" { + // Checks if the password is correct. + if !fb.CheckPasswordHash(cred.Password, u.Password) { + return http.StatusForbidden, nil + } } c.User = u @@ -171,6 +175,16 @@ func validateAuth(c *fb.Context, r *http.Request) (bool, *fb.User) { return true, c.User } + // If proxy auth is used do not verify the JWT token if the header is provided. + if c.AuthMethod == "proxy" { + u, err := c.Store.Users.GetByUsername(r.Header.Get(c.LoginHeader), c.NewFS) + if err != nil { + return false, nil + } + c.User = u; + return true, c.User + } + keyFunc := func(token *jwt.Token) (interface{}, error) { return c.Key, nil } @@ -194,3 +208,4 @@ func validateAuth(c *fb.Context, r *http.Request) (bool, *fb.User) { c.User = u return true, u } +