From 1db210a24fef20dca0129884eefad84c07af9d99 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 5 Jan 2019 23:56:22 +0000 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=A7=BC:=20remove=20comment=20[ci=20?= =?UTF-8?q?skip]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Former-commit-id: 6560b8172709c5e8809b734eaf82139635c24905 [formerly c172a284c4c2bce5d7f82a13dda7cdc78d5c7d66] [formerly b75b0b28ef23b9e341628054252f32eb08f9933c [formerly c830b80269f1cc04886e916b840937e4436f72ca]] Former-commit-id: b482c5c90f95166aaf3c368d136f23c67531d6a7 [formerly 52b0e3434d83a82f55aa6a0157a35578494b5aa0] Former-commit-id: d1e5d7262a89f20f80274be277c9e388af56f20e --- settings/settings.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/settings.go b/settings/settings.go index 11991a66..60285721 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -17,7 +17,7 @@ type Settings struct { Branding Branding `json:"branding"` Commands map[string][]string `json:"commands"` Shell []string `json:"shell"` - Rules []rules.Rule `json:"rules"` // TODO: use this add to cli + Rules []rules.Rule `json:"rules"` } // Server settings. From 3890a9a416c8bd1a5d329ddbb5284cfe1f6f5910 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 09:24:09 +0000 Subject: [PATCH 02/10] fix: do not read whole file on listings License: MIT Signed-off-by: Henrique Dias Former-commit-id: c630ed523a9f599c3dd1eda5930baa020d0b9911 [formerly f89f524d2979e43d6c2fd99903552bfe89addbc1] [formerly ba05ce7e5c730a04681910d87378dcdf3acc809b [formerly 28b326ab1df32f41234c0d48f3555f1a18e2ffeb]] Former-commit-id: cc4832f376eb2e5d5c99dfdac9b1966448b4e48f [formerly f254f221fa44f016a8f22cb3414580bb3a57750b] Former-commit-id: 66c66d82ade69e62d32e05f8350166b2cdc3bb12 --- files/file.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/files/file.go b/files/file.go index b50dc929..3dbb092a 100644 --- a/files/file.go +++ b/files/file.go @@ -76,7 +76,7 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) { return file, file.readListing(opts.Checker) } - err = file.detectType(opts.Modify) + err = file.detectType(opts.Modify, true) if err != nil { return nil, err } @@ -126,7 +126,7 @@ func (i *FileInfo) Checksum(algo string) error { return nil } -func (i *FileInfo) detectType(modify bool) error { +func (i *FileInfo) detectType(modify, saveContent bool) error { reader, err := i.Fs.Open(i.Path) if err != nil { return err @@ -160,17 +160,20 @@ func (i *FileInfo) detectType(modify bool) error { return nil default: i.Type = "text" - afs := &afero.Afero{Fs: i.Fs} - content, err := afs.ReadFile(i.Path) - if err != nil { - return err - } if !modify { i.Type = "textImmutable" } - i.Content = string(content) + if saveContent { + afs := &afero.Afero{Fs: i.Fs} + content, err := afs.ReadFile(i.Path) + if err != nil { + return err + } + + i.Content = string(content) + } } return nil @@ -238,7 +241,7 @@ func (i *FileInfo) readListing(checker rules.Checker) error { } else { listing.NumFiles++ - err := file.detectType(true) + err := file.detectType(true, false) if err != nil { return err } From 70042f44894c708453f73600c0cf28eb4ec1c21c Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 09:29:23 +0000 Subject: [PATCH 03/10] fix: allow embedding into iframes (close #550) License: MIT Signed-off-by: Henrique Dias Former-commit-id: fd99b98b820e6d7bfe0e5e2c7cb0371e19497c57 [formerly 9c306af4416794e5a2e0d9165960617cfeca81e1] [formerly 9267510624a99cc72cf29ba671cca797d2f441e3 [formerly 2d858e6738a1c3704cc5e894292b764b3fae7d9c]] Former-commit-id: 3004c1182c71f3eb094d2ce1f60e5ec4364d00f8 [formerly ff39e10768468f8eb2580fae4aed5f9242925dba] Former-commit-id: 0f7df8698160a72d74d6bdac41573b8c9c48cc4f --- http/static.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/http/static.go b/http/static.go index 6e84be85..bca3821e 100644 --- a/http/static.go +++ b/http/static.go @@ -85,9 +85,7 @@ func getStaticHandlers(storage *storage.Storage) (http.Handler, http.Handler) { return http.StatusNotFound, nil } - w.Header().Set("x-frame-options", "SAMEORIGIN") w.Header().Set("x-xss-protection", "1; mode=block") - return handleWithStaticData(w, r, d, box, "index.html", "text/html; charset=utf-8") }, "", storage) From a21198be118b676f5edc6326f6d741a9c467048d Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 10:06:39 +0000 Subject: [PATCH 04/10] feat: update frontend License: MIT Signed-off-by: Henrique Dias Former-commit-id: abe6d7d161c5623b479a0331bfb035bad0e3ac2b [formerly c7de783f07988d163a19f5c4f4274de029fbc4dd] [formerly 776a733a98306debc757ca1048233b24d7823987 [formerly d97bafb53b4a7bae1ba7dd507b62ab70e4aca017]] Former-commit-id: d405dd0600ad15bc6956c417f8894b8c51efa805 [formerly e9499a12c629884dd194e9ea4867fb07f3500735] Former-commit-id: 36df4acbca806758aa1d031fa1e9f4ae041e5830 --- frontend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend b/frontend index 95fc3dfd..7a6397af 160000 --- a/frontend +++ b/frontend @@ -1 +1 @@ -Subproject commit 95fc3dfdfbe21b1d55538add66bf0d5f38197320 +Subproject commit 7a6397af22380eec65f20836d713256b5397d182 From d821418bca95509efc12ccd5f57ed1f9301d04ea Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 12:01:44 +0000 Subject: [PATCH 05/10] feat: update frontend License: MIT Signed-off-by: Henrique Dias Former-commit-id: 3454ae3d39d1739b4c586f099442894cc64b4473 [formerly e2024dbdb835a464b91b843d73ffbbb5ca84d190] [formerly 5ed6bc9695aa06e86ad6c67afec1af85d332dacf [formerly 22f2287bc3416c3b241db92fd9f2173043c6942a]] Former-commit-id: f1a65e8d41ce28cc9d9d849d14ae407e48c58535 [formerly 1000226a97885b3e0a88aa3f0afaa7901ad4166d] Former-commit-id: ce067696dd15c98f472d8cbdd77e764a6a1efe0c --- frontend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend b/frontend index 7a6397af..0e7d4ef1 160000 --- a/frontend +++ b/frontend @@ -1 +1 @@ -Subproject commit 7a6397af22380eec65f20836d713256b5397d182 +Subproject commit 0e7d4ef110ee550375d4bf15dfa9ded70214076a From f8799443466faeab3e0292aaff06fea0291ac287 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 12:26:48 +0000 Subject: [PATCH 06/10] feat: dont persist server data on database License: MIT Signed-off-by: Henrique Dias Former-commit-id: 2ba9d0a5d060380a73ec1b46901a6f16a5e985a1 [formerly 1cd1c62eaba87c166d8fe10baebfe27bce0448a0] [formerly 3d1721671fb760a4b99aa66fe7d5a8d00579b17c [formerly a8ff679ae3beec9df9e3f89d253002f8b1b4250a]] Former-commit-id: ff84b7c9f8982292219b6c673ecbe60b231cb567 [formerly 2cf251aa108b3415e766fd05dd268ccd05f0ee1c] Former-commit-id: f112fdbd48836c509981cb93f41a47ff0eff77eb --- cmd/config.go | 12 --- cmd/config_init.go | 7 -- cmd/config_set.go | 10 --- cmd/root.go | 136 +++++++++++----------------------- cmd/utils.go | 9 +++ settings/settings.go | 10 --- storage/bolt/importer/conf.go | 5 -- 7 files changed, 53 insertions(+), 136 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index e03fb354..bca203c5 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -35,12 +35,6 @@ func addConfigFlags(cmd *cobra.Command) { cmd.Flags().BoolP("signup", "s", false, "allow users to signup") cmd.Flags().String("shell", "", "shell command to which other commands should be appended") - cmd.Flags().StringP("address", "a", "127.0.0.1", "default address to listen to") - cmd.Flags().StringP("log", "l", "stderr", "log output") - cmd.Flags().IntP("port", "p", 0, "default port to listen to") - cmd.Flags().String("tls.cert", "", "tls certificate path") - cmd.Flags().String("tls.key", "", "tls key path") - cmd.Flags().String("auth.method", string(auth.MethodJSONAuth), "authentication type") cmd.Flags().String("auth.header", "", "HTTP header for auth.method=proxy") @@ -101,12 +95,6 @@ func printSettings(s *settings.Settings, auther auth.Auther) { fmt.Fprintf(w, "Sign up:\t%t\n", s.Signup) fmt.Fprintf(w, "Auth method:\t%s\n", s.AuthMethod) fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(s.Shell, " ")) - fmt.Fprintf(w, "Log:\t%s\t\n", s.Log) - fmt.Fprintln(w, "\nServer:") - fmt.Fprintf(w, "\tAddress:\t%s\n", s.Server.Address) - fmt.Fprintf(w, "\tPort:\t%d\n", s.Server.Port) - fmt.Fprintf(w, "\tTLS Cert:\t%s\n", s.Server.TLSCert) - fmt.Fprintf(w, "\tTLS Key:\t%s\n", s.Server.TLSKey) fmt.Fprintln(w, "\nBranding:") fmt.Fprintf(w, "\tName:\t%s\n", s.Branding.Name) fmt.Fprintf(w, "\tFiles override:\t%s\n", s.Branding.Files) diff --git a/cmd/config_init.go b/cmd/config_init.go index cec455a0..f2e46c8a 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -43,17 +43,10 @@ override the options.`, s := &settings.Settings{ Key: generateRandomBytes(64), // 256 bit BaseURL: mustGetString(cmd, "baseURL"), - Log: mustGetString(cmd, "log"), Signup: mustGetBool(cmd, "signup"), Shell: strings.Split(strings.TrimSpace(mustGetString(cmd, "shell")), " "), AuthMethod: authMethod, Defaults: defaults, - Server: settings.Server{ - Address: mustGetString(cmd, "address"), - Port: mustGetInt(cmd, "port"), - TLSCert: mustGetString(cmd, "tls.cert"), - TLSKey: mustGetString(cmd, "tls.key"), - }, Branding: settings.Branding{ Name: mustGetString(cmd, "branding.name"), DisableExternal: mustGetBool(cmd, "branding.disableExternal"), diff --git a/cmd/config_set.go b/cmd/config_set.go index b2d4658a..b5e7b510 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -44,16 +44,6 @@ you want to change.`, s.Branding.DisableExternal = mustGetBool(cmd, flag.Name) case "branding.files": s.Branding.Files = mustGetString(cmd, flag.Name) - case "log": - s.Log = mustGetString(cmd, flag.Name) - case "address": - s.Server.Address = mustGetString(cmd, flag.Name) - case "port": - s.Server.Port = mustGetInt(cmd, flag.Name) - case "tls.cert": - s.Server.TLSCert = mustGetString(cmd, flag.Name) - case "tls.key": - s.Server.TLSKey = mustGetString(cmd, flag.Name) } }) diff --git a/cmd/root.go b/cmd/root.go index 9444dc4c..c7f01598 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,7 +1,6 @@ package cmd import ( - "crypto/rand" "crypto/tls" "errors" "io/ioutil" @@ -14,12 +13,10 @@ import ( "github.com/asdine/storm" "github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/settings" - "github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/users" fbhttp "github.com/filebrowser/filebrowser/v2/http" "github.com/spf13/cobra" - "github.com/spf13/pflag" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) @@ -30,12 +27,12 @@ var ( func init() { rootCmd.PersistentFlags().StringVarP(&databasePath, "database", "d", "./filebrowser.db", "path to the database") - rootCmd.Flags().StringP("address", "a", "", "address to listen on (default comes from database)") - rootCmd.Flags().StringP("log", "l", "", "log output (default comes from database)") - rootCmd.Flags().IntP("port", "p", 0, "port to listen on (default comes from database)") - rootCmd.Flags().StringP("cert", "c", "", "tls certificate (default comes from database)") - rootCmd.Flags().StringP("key", "k", "", "tls key (default comes from database)") - rootCmd.Flags().StringP("scope", "s", "", "scope for users") + rootCmd.Flags().StringP("address", "a", "127.0.0.1", "address to listen on") + rootCmd.Flags().StringP("log", "l", "stdout", "log output") + rootCmd.Flags().IntP("port", "p", 80, "port to listen on") + rootCmd.Flags().StringP("cert", "c", "", "tls certificate") + rootCmd.Flags().StringP("key", "k", "", "tls key") + rootCmd.Flags().StringP("scope", "s", "", "root scope to which user's scope are relative too") } var rootCmd = &cobra.Command{ @@ -46,29 +43,12 @@ manage your user and all the configurations without accessing the web interface. If you've never run File Browser, you will need to create the database. -See 'filebrowser help config init' for more information. - -This command is used to start up the server. By default it starts listening -on localhost on a random port unless specified otherwise in the database or -via flags. - -Use the available flags to override the database/default options. These flags -values won't be persisted to the database. To persist configuration to the database -use the command 'filebrowser config set'.`, - Run: func(cmd *cobra.Command, args []string) { - if _, err := os.Stat(databasePath); os.IsNotExist(err) { - quickSetup(cmd) - } - - db := getDB() - defer db.Close() - st := getStorage(db) - startServer(cmd, st) - }, +See 'filebrowser help config init' for more information.`, + Run: serveAndListen, } -func setupLogger(s *settings.Settings) { - switch s.Log { +func serveAndListen(cmd *cobra.Command, args []string) { + switch logMethod := mustGetString(cmd, "log"); logMethod { case "stdout": log.SetOutput(os.Stdout) case "stderr": @@ -77,29 +57,46 @@ func setupLogger(s *settings.Settings) { log.SetOutput(ioutil.Discard) default: log.SetOutput(&lumberjack.Logger{ - Filename: s.Log, + Filename: logMethod, MaxSize: 100, MaxAge: 14, MaxBackups: 10, }) } -} -func serverVisitAndReplace(cmd *cobra.Command, s *settings.Settings) { - cmd.Flags().Visit(func(flag *pflag.Flag) { - switch flag.Name { - case "log": - s.Log = mustGetString(cmd, flag.Name) - case "address": - s.Server.Address = mustGetString(cmd, flag.Name) - case "port": - s.Server.Port = mustGetInt(cmd, flag.Name) - case "cert": - s.Server.TLSCert = mustGetString(cmd, flag.Name) - case "key": - s.Server.TLSKey = mustGetString(cmd, flag.Name) - } - }) + if _, err := os.Stat(databasePath); os.IsNotExist(err) { + quickSetup(cmd) + } + + db := getDB() + defer db.Close() + st := getStorage(db) + + port := mustGetInt(cmd, "port") + address := mustGetString(cmd, "address") + cert := mustGetString(cmd, "cert") + key := mustGetString(cmd, "key") + + handler, err := fbhttp.NewHandler(st) + checkErr(err) + + var listener net.Listener + + if key != "" && cert != "" { + cer, err := tls.LoadX509KeyPair(cert, key) + checkErr(err) + config := &tls.Config{Certificates: []tls.Certificate{cer}} + listener, err = tls.Listen("tcp", address+":"+strconv.Itoa(port), config) + checkErr(err) + } else { + listener, err = net.Listen("tcp", address+":"+strconv.Itoa(port)) + checkErr(err) + } + + log.Println("Listening on", listener.Addr().String()) + if err := http.Serve(listener, handler); err != nil { + log.Fatal(err) + } } func quickSetup(cmd *cobra.Command) { @@ -115,15 +112,8 @@ func quickSetup(cmd *cobra.Command) { set := &settings.Settings{ Key: generateRandomBytes(64), // 256 bit BaseURL: "", - Log: "stderr", Signup: false, AuthMethod: auth.MethodJSONAuth, - Server: settings.Server{ - Port: 0, - Address: "127.0.0.1", - TLSCert: mustGetString(cmd, "cert"), - TLSKey: mustGetString(cmd, "key"), - }, Defaults: settings.UserDefaults{ Scope: scope, Locale: "en", @@ -140,7 +130,6 @@ func quickSetup(cmd *cobra.Command) { }, } - serverVisitAndReplace(cmd, set) st := getStorage(db) err = st.Settings.Save(set) @@ -164,40 +153,3 @@ func quickSetup(cmd *cobra.Command) { err = st.Users.Save(user) checkErr(err) } - -func startServer(cmd *cobra.Command, st *storage.Storage) { - settings, err := st.Settings.Get() - checkErr(err) - - serverVisitAndReplace(cmd, settings) - setupLogger(settings) - - handler, err := fbhttp.NewHandler(st) - checkErr(err) - - var listener net.Listener - - if settings.Server.TLSKey != "" && settings.Server.TLSCert != "" { - cer, err := tls.LoadX509KeyPair(settings.Server.TLSCert, settings.Server.TLSKey) - checkErr(err) - config := &tls.Config{Certificates: []tls.Certificate{cer}} - listener, err = tls.Listen("tcp", settings.Server.Address+":"+strconv.Itoa(settings.Server.Port), config) - checkErr(err) - } else { - listener, err = net.Listen("tcp", settings.Server.Address+":"+strconv.Itoa(settings.Server.Port)) - checkErr(err) - } - - log.Println("Listening on", listener.Addr().String()) - if err := http.Serve(listener, handler); err != nil { - log.Fatal(err) - } -} - -func generateRandomBytes(n int) []byte { - b := make([]byte, n) - _, err := rand.Read(b) - checkErr(err) - // Note that err == nil only if we read len(b) bytes. - return b -} diff --git a/cmd/utils.go b/cmd/utils.go index 8b2520dd..70e63413 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -1,6 +1,7 @@ package cmd import ( + "crypto/rand" "errors" "os" @@ -53,3 +54,11 @@ func getDB() *storm.DB { func getStorage(db *storm.DB) *storage.Storage { return bolt.NewStorage(db) } + +func generateRandomBytes(n int) []byte { + b := make([]byte, n) + _, err := rand.Read(b) + checkErr(err) + // Note that err == nil only if we read len(b) bytes. + return b +} diff --git a/settings/settings.go b/settings/settings.go index 60285721..b287be8d 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -9,8 +9,6 @@ type AuthMethod string type Settings struct { Key []byte `json:"key"` BaseURL string `json:"baseURL"` - Log string `json:"log"` - Server Server `json:"server"` Signup bool `json:"signup"` Defaults UserDefaults `json:"defaults"` AuthMethod AuthMethod `json:"authMethod"` @@ -20,14 +18,6 @@ type Settings struct { Rules []rules.Rule `json:"rules"` } -// Server settings. -type Server struct { - Port int `json:"port"` - Address string `json:"address"` - TLSCert string `json:"tlsCert"` - TLSKey string `json:"tlsKey"` -} - // GetRules implements rules.Provider. func (s *Settings) GetRules() []rules.Rule { return s.Rules diff --git a/storage/bolt/importer/conf.go b/storage/bolt/importer/conf.go index efd98df7..335f4148 100644 --- a/storage/bolt/importer/conf.go +++ b/storage/bolt/importer/conf.go @@ -111,7 +111,6 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error { s := &settings.Settings{ Key: key, BaseURL: cfg.BaseURL, - Log: cfg.Log, Signup: false, Defaults: settings.UserDefaults{ Scope: cfg.Defaults.Scope, @@ -129,10 +128,6 @@ func importConf(db *storm.DB, path string, sto *storage.Storage) error { Download: true, }, }, - Server: settings.Server{ - Address: cfg.Address, - Port: cfg.Port, - }, } var auther auth.Auther From 94619341f21421c3d56cd8f93f5e9accd6b83a89 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 12:41:52 +0000 Subject: [PATCH 07/10] feat: simplify future changes License: MIT Signed-off-by: Henrique Dias Former-commit-id: 1fef9a9903b88ce216c4863a2c13d18030883f01 [formerly ab968c3e733db147d0e185934f03e7f965e03fde] [formerly a8e8e77ed08ca600abc156ba1cd57aff1af9d5c5 [formerly 12eb050cc22baeda292a25034821aa0d7640ca84]] Former-commit-id: eeb1864fb4c2c0d981c657a54c2d1e2a83a260e7 [formerly 09d661a1172e93b8e6a627dff2870b1e1c5903c6] Former-commit-id: 5974a6e8e1309855e58160e6fa827de6dc5b9605 --- cmd/users.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/users.go b/cmd/users.go index e497e82b..f49f46e4 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -94,35 +94,35 @@ func getUserDefaults(cmd *cobra.Command, defaults *settings.UserDefaults, all bo visit := func(flag *pflag.Flag) { switch flag.Name { case "scope": - defaults.Scope = mustGetString(cmd, "scope") + defaults.Scope = mustGetString(cmd, flag.Name) case "locale": - defaults.Locale = mustGetString(cmd, "locale") + defaults.Locale = mustGetString(cmd, flag.Name) case "viewMode": defaults.ViewMode = getViewMode(cmd) case "perm.admin": - defaults.Perm.Admin = mustGetBool(cmd, "perm.admin") + defaults.Perm.Admin = mustGetBool(cmd, flag.Name) case "perm.execute": - defaults.Perm.Execute = mustGetBool(cmd, "perm.execute") + defaults.Perm.Execute = mustGetBool(cmd, flag.Name) case "perm.create": - defaults.Perm.Create = mustGetBool(cmd, "perm.create") + defaults.Perm.Create = mustGetBool(cmd, flag.Name) case "perm.rename": - defaults.Perm.Rename = mustGetBool(cmd, "perm.rename") + defaults.Perm.Rename = mustGetBool(cmd, flag.Name) case "perm.modify": - defaults.Perm.Modify = mustGetBool(cmd, "perm.modify") + defaults.Perm.Modify = mustGetBool(cmd, flag.Name) case "perm.delete": - defaults.Perm.Delete = mustGetBool(cmd, "perm.delete") + defaults.Perm.Delete = mustGetBool(cmd, flag.Name) case "perm.share": - defaults.Perm.Share = mustGetBool(cmd, "perm.share") + defaults.Perm.Share = mustGetBool(cmd, flag.Name) case "perm.download": - defaults.Perm.Download = mustGetBool(cmd, "perm.download") + defaults.Perm.Download = mustGetBool(cmd, flag.Name) case "commands": - commands, err := cmd.Flags().GetStringSlice("commands") + commands, err := cmd.Flags().GetStringSlice(flag.Name) checkErr(err) defaults.Commands = commands case "sorting.by": - defaults.Sorting.By = mustGetString(cmd, "sorting.by") + defaults.Sorting.By = mustGetString(cmd, flag.Name) case "sorting.asc": - defaults.Sorting.Asc = mustGetBool(cmd, "sorting.asc") + defaults.Sorting.Asc = mustGetBool(cmd, flag.Name) } } From 70733ff846e4221eef6dee84cc4568d8a7060a47 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 12:57:35 +0000 Subject: [PATCH 08/10] feat: change default to 8080 License: MIT Signed-off-by: Henrique Dias Former-commit-id: 4bcba1cc1d4f275c68b1e3a32045f6ec1136d264 [formerly 990ef78e797dc4a9828a6dd8c5c7162e06c6af3f] [formerly 1a4f246dc04fc76b0dd9656beb010d03fcef8045 [formerly e22598a1260195653de23d0f972580f8d5bb020c]] Former-commit-id: 649df28a7201a0fefbd57b6faf56046fbf46c7b5 [formerly 7c763bae4fb9072920cc0907102583e15d1a7afc] Former-commit-id: 163cfb1164e3f2be099722b013598fd9aba6611a --- cmd/root.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index c7f01598..b895fa94 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -29,7 +29,7 @@ func init() { rootCmd.Flags().StringP("address", "a", "127.0.0.1", "address to listen on") rootCmd.Flags().StringP("log", "l", "stdout", "log output") - rootCmd.Flags().IntP("port", "p", 80, "port to listen on") + rootCmd.Flags().IntP("port", "p", 8080, "port to listen on") rootCmd.Flags().StringP("cert", "c", "", "tls certificate") rootCmd.Flags().StringP("key", "k", "", "tls key") rootCmd.Flags().StringP("scope", "s", "", "root scope to which user's scope are relative too") From f2d952bf0e00867296e29ebb264ff785da1519a9 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 12:59:53 +0000 Subject: [PATCH 09/10] fix: dont fail when can't detect file type (#609) License: MIT Signed-off-by: Henrique Dias Former-commit-id: 3dbe943b6d64bf7d3f6c6d8970d2608438e4c2bc [formerly d5238a25f0e31dee84367a5e27b6f68ab50050d6] [formerly 297d8be6b3aa5e5411634286ba4faff256fd6837 [formerly 07f3ee38e50ba6cef7a515b3fad1a37921fa1160]] Former-commit-id: 5a568c9eff47b5fa54d3956ecc2d2a55cb31fc8f [formerly 8d7f78cbf47a8a84f02324fdd858782e4b67b0e0] Former-commit-id: 1343ba84d9c8952bd9e210cf7f23b70129236c81 --- files/file.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/files/file.go b/files/file.go index 3dbb092a..f7253a1e 100644 --- a/files/file.go +++ b/files/file.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "hash" "io" + "log" "mime" "net/http" "os" @@ -127,16 +128,24 @@ func (i *FileInfo) Checksum(algo string) error { } func (i *FileInfo) detectType(modify, saveContent bool) error { + // failing to detect the type should not return error. + // imagine the situation where a file in a dir with thousands + // of files couldn't be opened: we'd have immediately + // a 500 even though it doesn't matter. So we just log it. reader, err := i.Fs.Open(i.Path) if err != nil { - return err + log.Print(err) + i.Type = "blob" + return nil } defer reader.Close() buffer := make([]byte, 512) n, err := reader.Read(buffer) if err != nil && err != io.EOF { - return err + log.Print(err) + i.Type = "blob" + return nil } mimetype := mime.TypeByExtension(i.Extension) From f1a89f5ec49717f767587b8d561c3de7befd19fc Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Jan 2019 13:01:42 +0000 Subject: [PATCH 10/10] feat: add global scope (#604) License: MIT Signed-off-by: Henrique Dias Former-commit-id: a44ebfc7a5687b5f51f0ff791335f66ab9f2e8e0 [formerly 9a044fadd8f2ebbb7dbb773273799c26a797f513] [formerly 7f374d016eaf756cfce215dd16a2274bbabe1915 [formerly f55f205ced82d7e05e0ffc158b302b5300409769]] Former-commit-id: 31015ddc5f4fc28c895743f6fe9dcf5488bb4b01 [formerly e439027304a1e49667fafde011e07d043ef0d2ee] Former-commit-id: 0394c60358673b56991364260b1cbe41fa457593 --- auth/auth.go | 5 ++--- auth/json.go | 10 ++-------- auth/none.go | 10 ++-------- auth/proxy.go | 12 +++--------- auth/storage.go | 10 ++-------- cmd/config_init.go | 1 - cmd/root.go | 20 ++++++++++++-------- cmd/rules.go | 2 +- cmd/users.go | 2 +- cmd/users_find.go | 10 ++++++---- cmd/users_update.go | 8 +++++--- http/auth.go | 4 ++-- http/public.go | 2 +- http/users.go | 6 +++--- settings/settings.go | 1 + storage/bolt/importer/users.go | 9 +-------- users/storage.go | 12 ++++++------ users/users.go | 17 ++++++++++------- 18 files changed, 60 insertions(+), 81 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index bcde03d7..86b56a04 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -3,13 +3,12 @@ package auth import ( "net/http" + "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/users" ) // Auther is the authentication interface. type Auther interface { // Auth is called to authenticate a request. - Auth(*http.Request) (*users.User, error) - // SetStorage attaches the Storage instance. - SetStorage(*users.Storage) + Auth(*http.Request, *users.Storage, *settings.Settings) (*users.User, error) } diff --git a/auth/json.go b/auth/json.go index ecf067a2..9bd86fe8 100644 --- a/auth/json.go +++ b/auth/json.go @@ -23,11 +23,10 @@ type jsonCred struct { // JSONAuth is a json implementaion of an Auther. type JSONAuth struct { ReCaptcha *ReCaptcha - storage *users.Storage } // Auth authenticates the user via a json in content body. -func (a *JSONAuth) Auth(r *http.Request) (*users.User, error) { +func (a *JSONAuth) Auth(r *http.Request, sto *users.Storage, set *settings.Settings) (*users.User, error) { var cred jsonCred if r.Body == nil { @@ -52,7 +51,7 @@ func (a *JSONAuth) Auth(r *http.Request) (*users.User, error) { } } - u, err := a.storage.Get(cred.Username) + u, err := sto.Get(set.Scope, cred.Username) if err != nil || !users.CheckPwd(cred.Password, u.Password) { return nil, os.ErrPermission } @@ -60,11 +59,6 @@ func (a *JSONAuth) Auth(r *http.Request) (*users.User, error) { return u, nil } -// SetStorage attaches the storage to the auther. -func (a *JSONAuth) SetStorage(s *users.Storage) { - a.storage = s -} - const reCaptchaAPI = "/recaptcha/api/siteverify" // ReCaptcha identifies a recaptcha conenction. diff --git a/auth/none.go b/auth/none.go index 0d3e2293..76312881 100644 --- a/auth/none.go +++ b/auth/none.go @@ -12,15 +12,9 @@ const MethodNoAuth settings.AuthMethod = "noauth" // NoAuth is no auth implementation of auther. type NoAuth struct { - storage *users.Storage } // Auth uses authenticates user 1. -func (a *NoAuth) Auth(r *http.Request) (*users.User, error) { - return a.storage.Get(1) -} - -// SetStorage attaches the storage to the auther. -func (a *NoAuth) SetStorage(s *users.Storage) { - a.storage = s +func (a *NoAuth) Auth(r *http.Request, sto *users.Storage, set *settings.Settings) (*users.User, error) { + return sto.Get(set.Scope, 1) } diff --git a/auth/proxy.go b/auth/proxy.go index f23b70fb..e3176bdd 100644 --- a/auth/proxy.go +++ b/auth/proxy.go @@ -14,22 +14,16 @@ const MethodProxyAuth settings.AuthMethod = "proxy" // ProxyAuth is a proxy implementation of an auther. type ProxyAuth struct { - Header string - storage *users.Storage + Header string } // Auth authenticates the user via an HTTP header. -func (a *ProxyAuth) Auth(r *http.Request) (*users.User, error) { +func (a *ProxyAuth) Auth(r *http.Request, sto *users.Storage, set *settings.Settings) (*users.User, error) { username := r.Header.Get(a.Header) - user, err := a.storage.Get(username) + user, err := sto.Get(set.Scope, username) if err == errors.ErrNotExist { return nil, os.ErrPermission } return user, err } - -// SetStorage attaches the storage to the auther. -func (a *ProxyAuth) SetStorage(s *users.Storage) { - a.storage = s -} diff --git a/auth/storage.go b/auth/storage.go index b5bd5e83..2cf63e05 100644 --- a/auth/storage.go +++ b/auth/storage.go @@ -22,15 +22,9 @@ func NewStorage(back StorageBackend, users *users.Storage) *Storage { return &Storage{back: back, users: users} } -// Get wraps a StorageBackend.Get and calls SetStorage on the auther. +// Get wraps a StorageBackend.Get. func (s *Storage) Get(t settings.AuthMethod) (Auther, error) { - auther, err := s.back.Get(t) - if err != nil { - return nil, err - } - - auther.SetStorage(s.users) - return auther, nil + return s.back.Get(t) } // Save wraps a StorageBackend.Save. diff --git a/cmd/config_init.go b/cmd/config_init.go index f2e46c8a..6c18628d 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -15,7 +15,6 @@ func init() { configCmd.AddCommand(configInitCmd) rootCmd.AddCommand(configInitCmd) addConfigFlags(configInitCmd) - configInitCmd.MarkFlagRequired("scope") } var configInitCmd = &cobra.Command{ diff --git a/cmd/root.go b/cmd/root.go index b895fa94..437acf2f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,12 +2,12 @@ package cmd import ( "crypto/tls" - "errors" "io/ioutil" "log" "net" "net/http" "os" + "path/filepath" "strconv" "github.com/asdine/storm" @@ -32,7 +32,7 @@ func init() { rootCmd.Flags().IntP("port", "p", 8080, "port to listen on") rootCmd.Flags().StringP("cert", "c", "", "tls certificate") rootCmd.Flags().StringP("key", "k", "", "tls key") - rootCmd.Flags().StringP("scope", "s", "", "root scope to which user's scope are relative too") + rootCmd.Flags().StringP("scope", "s", ".", "scope to prepend to a user's scope when it is relative") } var rootCmd = &cobra.Command{ @@ -76,6 +76,15 @@ func serveAndListen(cmd *cobra.Command, args []string) { address := mustGetString(cmd, "address") cert := mustGetString(cmd, "cert") key := mustGetString(cmd, "key") + scope := mustGetString(cmd, "scope") + + scope, err := filepath.Abs(scope) + checkErr(err) + settings, err := st.Settings.Get() + checkErr(err) + settings.Scope = scope + err = st.Settings.Save(settings) + checkErr(err) handler, err := fbhttp.NewHandler(st) checkErr(err) @@ -100,11 +109,6 @@ func serveAndListen(cmd *cobra.Command, args []string) { } func quickSetup(cmd *cobra.Command) { - scope := mustGetString(cmd, "scope") - if scope == "" { - panic(errors.New("scope flag must be set for quick setup")) - } - db, err := storm.Open(databasePath) checkErr(err) defer db.Close() @@ -115,7 +119,7 @@ func quickSetup(cmd *cobra.Command) { Signup: false, AuthMethod: auth.MethodJSONAuth, Defaults: settings.UserDefaults{ - Scope: scope, + Scope: ".", Locale: "en", Perm: users.Permissions{ Admin: false, diff --git a/cmd/rules.go b/cmd/rules.go index 99f20814..2a7d219d 100644 --- a/cmd/rules.go +++ b/cmd/rules.go @@ -39,7 +39,7 @@ func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), glo id := getUserIdentifier(cmd) if id != nil { - user, err := st.Users.Get(id) + user, err := st.Users.Get("", id) checkErr(err) if users != nil { diff --git a/cmd/users.go b/cmd/users.go index f49f46e4..3afdba86 100644 --- a/cmd/users.go +++ b/cmd/users.go @@ -77,7 +77,7 @@ func addUserFlags(cmd *cobra.Command) { cmd.Flags().Bool("sorting.asc", false, "sorting by ascending order") cmd.Flags().Bool("lockPassword", false, "lock password") cmd.Flags().StringSlice("commands", nil, "a list of the commands a user can execute") - cmd.Flags().String("scope", "", "scope for users") + cmd.Flags().String("scope", ".", "scope for users") cmd.Flags().String("locale", "en", "locale for users") cmd.Flags().String("viewMode", string(users.ListViewMode), "view mode for users") } diff --git a/cmd/users_find.go b/cmd/users_find.go index 9fdd92b6..126c7354 100644 --- a/cmd/users_find.go +++ b/cmd/users_find.go @@ -32,19 +32,21 @@ var findUsers = func(cmd *cobra.Command, args []string) { defer db.Close() st := getStorage(db) + settings, err := st.Settings.Get() + checkErr(err) + username, _ := cmd.Flags().GetString("username") id, _ := cmd.Flags().GetUint("id") - var err error var list []*users.User var user *users.User if username != "" { - user, err = st.Users.Get(username) + user, err = st.Users.Get(settings.Scope, username) } else if id != 0 { - user, err = st.Users.Get(id) + user, err = st.Users.Get(settings.Scope, id) } else { - list, err = st.Users.Gets() + list, err = st.Users.Gets(settings.Scope) } checkErr(err) diff --git a/cmd/users_update.go b/cmd/users_update.go index ebeef419..f0aa27ba 100644 --- a/cmd/users_update.go +++ b/cmd/users_update.go @@ -26,17 +26,19 @@ options you want to change.`, defer db.Close() st := getStorage(db) + set, err := st.Settings.Get() + checkErr(err) + id, _ := cmd.Flags().GetUint("id") username := mustGetString(cmd, "username") password := mustGetString(cmd, "password") var user *users.User - var err error if id != 0 { - user, err = st.Users.Get(id) + user, err = st.Users.Get(set.Scope, id) } else { - user, err = st.Users.Get(username) + user, err = st.Users.Get(set.Scope, username) } checkErr(err) diff --git a/http/auth.go b/http/auth.go index 9ea0a889..2e01ddf0 100644 --- a/http/auth.go +++ b/http/auth.go @@ -67,7 +67,7 @@ func withUser(fn handleFunc) handleFunc { w.Header().Add("X-Renew-Token", "true") } - d.user, err = d.store.Users.Get(tk.User.ID) + d.user, err = d.store.Users.Get(d.settings.Scope, tk.User.ID) if err != nil { return http.StatusInternalServerError, err } @@ -91,7 +91,7 @@ var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, e return http.StatusInternalServerError, err } - user, err := auther.Auth(r) + user, err := auther.Auth(r, d.store.Users, d.Settings) if err == os.ErrPermission { return http.StatusForbidden, nil } else if err != nil { diff --git a/http/public.go b/http/public.go index 656e3698..afab24a6 100644 --- a/http/public.go +++ b/http/public.go @@ -13,7 +13,7 @@ var withHashFile = func(fn handleFunc) handleFunc { return errToStatus(err), err } - user, err := d.store.Users.Get(link.UserID) + user, err := d.store.Users.Get(d.settings.Scope, link.UserID) if err != nil { return errToStatus(err), err } diff --git a/http/users.go b/http/users.go index 545b2536..ad547435 100644 --- a/http/users.go +++ b/http/users.go @@ -61,7 +61,7 @@ func withSelfOrAdmin(fn handleFunc) handleFunc { } var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - users, err := d.store.Users.Gets() + users, err := d.store.Users.Gets(d.settings.Scope) if err != nil { return http.StatusInternalServerError, err } @@ -78,7 +78,7 @@ var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d * }) var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { - u, err := d.store.Users.Get(d.raw.(uint)) + u, err := d.store.Users.Get(d.settings.Scope, d.raw.(uint)) if err == errors.ErrNotExist { return http.StatusNotFound, err } @@ -147,7 +147,7 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request req.Data.Password, err = users.HashPwd(req.Data.Password) } else { var suser *users.User - suser, err = d.store.Users.Get(d.raw.(uint)) + suser, err = d.store.Users.Get(d.settings.Scope, d.raw.(uint)) req.Data.Password = suser.Password } diff --git a/settings/settings.go b/settings/settings.go index b287be8d..57d4863d 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -9,6 +9,7 @@ type AuthMethod string type Settings struct { Key []byte `json:"key"` BaseURL string `json:"baseURL"` + Scope string `json:"scope"` Signup bool `json:"signup"` Defaults UserDefaults `json:"defaults"` AuthMethod AuthMethod `json:"authMethod"` diff --git a/storage/bolt/importer/users.go b/storage/bolt/importer/users.go index 660403b4..5a98ffb9 100644 --- a/storage/bolt/importer/users.go +++ b/storage/bolt/importer/users.go @@ -3,7 +3,6 @@ package importer import ( "encoding/json" "fmt" - "path/filepath" "github.com/asdine/storm" "github.com/filebrowser/filebrowser/v2/rules" @@ -52,7 +51,6 @@ func readOldUsers(db *storm.DB) ([]*oldUser, error) { } func convertUsersToNew(old []*oldUser) ([]*users.User, error) { - var err error list := []*users.User{} for _, oldUser := range old { @@ -82,12 +80,7 @@ func convertUsersToNew(old []*oldUser) ([]*users.User, error) { user.Rules = append(user.Rules, *rule) } - user.Scope, err = filepath.Abs(user.Scope) - if err != nil { - return nil, err - } - - err = user.Clean() + err := user.Clean("") if err != nil { return nil, err } diff --git a/users/storage.go b/users/storage.go index 1366b968..ce3e7514 100644 --- a/users/storage.go +++ b/users/storage.go @@ -36,7 +36,7 @@ func NewStorage(back StorageBackend) *Storage { // Get allows you to get a user by its name or username. The provided // id must be a string for username lookup or a uint for id lookup. If id // is neither, a ErrInvalidDataType will be returned. -func (s *Storage) Get(id interface{}) (*User, error) { +func (s *Storage) Get(baseScope string, id interface{}) (*User, error) { var ( user *User err error @@ -55,19 +55,19 @@ func (s *Storage) Get(id interface{}) (*User, error) { return nil, err } - user.Clean() + user.Clean(baseScope) return user, err } // Gets gets a list of all users. -func (s *Storage) Gets() ([]*User, error) { +func (s *Storage) Gets(baseScope string) ([]*User, error) { users, err := s.back.Gets() if err != nil { return nil, err } for _, user := range users { - user.Clean() + user.Clean(baseScope) } return users, err @@ -75,7 +75,7 @@ func (s *Storage) Gets() ([]*User, error) { // Update updates a user in the database. func (s *Storage) Update(user *User, fields ...string) error { - err := user.Clean(fields...) + err := user.Clean("", fields...) if err != nil { return err } @@ -93,7 +93,7 @@ func (s *Storage) Update(user *User, fields ...string) error { // Save saves the user in a storage. func (s *Storage) Save(user *User) error { - if err := user.Clean(); err != nil { + if err := user.Clean(""); err != nil { return err } diff --git a/users/users.go b/users/users.go index 8d530048..387a4eaa 100644 --- a/users/users.go +++ b/users/users.go @@ -1,10 +1,11 @@ package users import ( - "github.com/filebrowser/filebrowser/v2/errors" "path/filepath" "regexp" + "github.com/filebrowser/filebrowser/v2/errors" + "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/rules" "github.com/spf13/afero" @@ -51,7 +52,7 @@ var checkableFields = []string{ // Clean cleans up a user and verifies if all its fields // are alright to be saved. -func (u *User) Clean(fields ...string) error { +func (u *User) Clean(baseScope string, fields ...string) error { if len(fields) == 0 { fields = checkableFields } @@ -66,10 +67,6 @@ func (u *User) Clean(fields ...string) error { if u.Password == "" { return errors.ErrEmptyPassword } - case "Scope": - if !filepath.IsAbs(u.Scope) { - return errors.ErrScopeIsRelative - } case "ViewMode": if u.ViewMode == "" { u.ViewMode = ListViewMode @@ -90,7 +87,13 @@ func (u *User) Clean(fields ...string) error { } if u.Fs == nil { - u.Fs = afero.NewBasePathFs(afero.NewOsFs(), u.Scope) + scope := u.Scope + + if !filepath.IsAbs(scope) { + scope = filepath.Join(baseScope, scope) + } + + u.Fs = afero.NewBasePathFs(afero.NewOsFs(), scope) } return nil