merge with master
License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
Former-commit-id: dfac33db9ca3454cd3af02a732a64a02a8268b01 [formerly 38be015f7b3376a3bc992725c1cb1a019c1843cb] [formerly 5a4e310b96ebc5895e64f8eeb2f592890d4b63b0 [formerly 2b8bd28158]]
Former-commit-id: a30bfcebe6bbc115990b8f8398171b32942a8767 [formerly 553fafa22b46b2689ece093f3b0cf85b3cff1c87]
Former-commit-id: 5ce047d4e3f5217a179685f8b2850d4eb157376e
			
			
				pull/726/head
			
			
		
						commit
						e5c150e83f
					
				|  | @ -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) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								auth/json.go
								
								
								
								
							
							
						
						
									
										10
									
								
								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.
 | ||||
|  |  | |||
							
								
								
									
										10
									
								
								auth/none.go
								
								
								
								
							
							
						
						
									
										10
									
								
								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) | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -16,7 +16,6 @@ func init() { | |||
| 	configCmd.AddCommand(configInitCmd) | ||||
| 	rootCmd.AddCommand(configInitCmd) | ||||
| 	addConfigFlags(configInitCmd) | ||||
| 	configInitCmd.MarkFlagRequired("scope") | ||||
| } | ||||
| 
 | ||||
| var configInitCmd = &cobra.Command{ | ||||
|  | @ -45,17 +44,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"), | ||||
|  |  | |||
|  | @ -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) | ||||
| 			} | ||||
| 		}) | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										379
									
								
								cmd/root.go
								
								
								
								
							
							
						
						
									
										379
									
								
								cmd/root.go
								
								
								
								
							|  | @ -1,30 +1,53 @@ | |||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"crypto/tls" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"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" | ||||
| 	homedir "github.com/mitchellh/go-homedir" | ||||
| 	"github.com/filebrowser/filebrowser/v2/settings" | ||||
| 	"github.com/filebrowser/filebrowser/v2/users" | ||||
| 	"github.com/mitchellh/go-homedir" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	//	"github.com/spf13/pflag"
 | ||||
| 	v "github.com/spf13/viper" | ||||
| 	lumberjack "gopkg.in/natefinch/lumberjack.v2" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	cfgFile string | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	f := rootCmd.Flags() | ||||
| 	pf := rootCmd.PersistentFlags() | ||||
| 
 | ||||
| 	f.StringVarP(&cfgFile, "config", "c", "", "config file (defaults are './.filebrowser[ext]', '$HOME/.filebrowser[ext]' or '/etc/filebrowser/.filebrowser[ext]')") | ||||
| 	vaddP(pf, "database", "d", "./filebrowser.db", "path to the database") | ||||
| 	vaddP(f, "address", "a", "127.0.0.1", "address to listen on") | ||||
| 	vaddP(f, "log", "l", "stdout", "log output") | ||||
| 	vaddP(f, "port", "p", 8080, "port to listen on") | ||||
| 	vaddP(f, "cert", "t", "", "tls certificate") | ||||
| 	vaddP(f, "key", "k", "", "tls key") | ||||
| 	vaddP(f, "scope", "s", ".", "scope to prepend to a user's scope when it is relative") | ||||
| 
 | ||||
| 	if err := v.BindPFlags(f); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := v.BindPFlags(pf); err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var rootCmd = &cobra.Command{ | ||||
| 	Use:   "filebrowser", | ||||
| 	Short: "A stylish web-based file browser", | ||||
|  | @ -33,89 +56,133 @@ 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) { | ||||
| 		db := getDB() | ||||
| 		defer db.Close() | ||||
| 		st := getStorage(db) | ||||
| 		startServer(st) | ||||
| 	}, | ||||
| See 'filebrowser help config init' for more information.`, | ||||
| 	Run: serveAndListen, | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	cfgFile string | ||||
| ) | ||||
| func serveAndListen(cmd *cobra.Command, args []string) { | ||||
| 	initConfig() | ||||
| 
 | ||||
| // POSSIBLE WORKAROUND TO IDENTIFY WHEN DEFAULT VALUES ARE BEING USED
 | ||||
| var defaults = struct { | ||||
| 	database string | ||||
| 	address  string | ||||
| 	log      string | ||||
| 	port     int | ||||
| 	scope    string | ||||
| 	admin    string | ||||
| }{ | ||||
| 	"./filebrowser.db", | ||||
| 	"127.0.0.1", | ||||
| 	"stderr", | ||||
| 	80, | ||||
| 	"/srv", | ||||
| 	"admin", | ||||
| 	switch logMethod := v.GetString("log"); logMethod { | ||||
| 	case "stdout": | ||||
| 		log.SetOutput(os.Stdout) | ||||
| 	case "stderr": | ||||
| 		log.SetOutput(os.Stderr) | ||||
| 	case "": | ||||
| 		log.SetOutput(ioutil.Discard) | ||||
| 	default: | ||||
| 		log.SetOutput(&lumberjack.Logger{ | ||||
| 			Filename:   logMethod, | ||||
| 			MaxSize:    100, | ||||
| 			MaxAge:     14, | ||||
| 			MaxBackups: 10, | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) { | ||||
| 		quickSetup(cmd) | ||||
| 	} | ||||
| 
 | ||||
| 	db := getDB() | ||||
| 	defer db.Close() | ||||
| 	st := getStorage(db) | ||||
| 
 | ||||
| 	port := v.GetInt("port") | ||||
| 	address := v.GetString("address") | ||||
| 	cert := v.GetString("cert") | ||||
| 	key := v.GetString("key") | ||||
| 	scope := v.GetString("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) | ||||
| 
 | ||||
| 	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 init() { | ||||
| 	cobra.OnInitialize(initConfig) | ||||
| 	//rootCmd.SetVersionTemplate("File Browser {{printf \"version %s\" .Version}}\n")
 | ||||
| func quickSetup(cmd *cobra.Command) { | ||||
| 	db, err := storm.Open(v.GetString("database")) | ||||
| 	checkErr(err) | ||||
| 	defer db.Close() | ||||
| 
 | ||||
| 	f := rootCmd.Flags() | ||||
| 	pf := rootCmd.PersistentFlags() | ||||
| 
 | ||||
| 	pf.StringVarP(&cfgFile, "config", "c", "", "config file (defaults are './.filebrowser[ext]', '$HOME/.filebrowser[ext]' or '/etc/filebrowser/.filebrowser[ext]')") | ||||
| 
 | ||||
| 	vaddP(pf, "database", "d", "./filebrowser.db", "path to the database") | ||||
| 
 | ||||
| 	vaddP(f, "address", "a", defaults.address, "address to listen on") | ||||
| 	vaddP(f, "log", "l", defaults.log, "log output") | ||||
| 	vaddP(f, "port", "p", defaults.port, "port to listen on") | ||||
| 	vaddP(f, "cert", "t", "", "tls certificate (default comes from database)") | ||||
| 	vaddP(f, "key", "k", "", "tls key (default comes from database)") | ||||
| 	vaddP(f, "scope", "s", defaults.scope, "scope for users") | ||||
| 	vaddP(f, "force", "f", false, "overwrite DB config with runtime params") | ||||
| 	vaddP(f, "admin", "f", defaults.admin, "first username") | ||||
| 	vaddP(f, "passwd", "f", "", "first username password hash") | ||||
| 	vaddP(f, "baseurl", "b", "", "base URL") | ||||
| 
 | ||||
| 	// Bind the full flag sets to the configuration
 | ||||
| 	if err := v.BindPFlags(f); err != nil { | ||||
| 		panic(err) | ||||
| 	set := &settings.Settings{ | ||||
| 		Key:        generateRandomBytes(64), // 256 bit
 | ||||
| 		BaseURL:    "", | ||||
| 		Signup:     false, | ||||
| 		AuthMethod: auth.MethodJSONAuth, | ||||
| 		Defaults: settings.UserDefaults{ | ||||
| 			Scope:  ".", | ||||
| 			Locale: "en", | ||||
| 			Perm: users.Permissions{ | ||||
| 				Admin:    false, | ||||
| 				Execute:  true, | ||||
| 				Create:   true, | ||||
| 				Rename:   true, | ||||
| 				Modify:   true, | ||||
| 				Delete:   true, | ||||
| 				Share:    true, | ||||
| 				Download: true, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	if err := v.BindPFlags(pf); err != nil { | ||||
| 		panic(err) | ||||
| 
 | ||||
| 	st := getStorage(db) | ||||
| 
 | ||||
| 	err = st.Settings.Save(set) | ||||
| 	checkErr(err) | ||||
| 
 | ||||
| 	err = st.Auth.Save(&auth.JSONAuth{}) | ||||
| 	checkErr(err) | ||||
| 
 | ||||
| 	password, err := users.HashPwd("admin") | ||||
| 	checkErr(err) | ||||
| 
 | ||||
| 	user := &users.User{ | ||||
| 		Username:     "admin", | ||||
| 		Password:     password, | ||||
| 		LockPassword: false, | ||||
| 	} | ||||
| 
 | ||||
| 	set.Defaults.Apply(user) | ||||
| 	user.Perm.Admin = true | ||||
| 
 | ||||
| 	err = st.Users.Save(user) | ||||
| 	checkErr(err) | ||||
| } | ||||
| 
 | ||||
| // initConfig reads in config file and ENV variables if set.
 | ||||
| func initConfig() { | ||||
| 	if cfgFile == "" { | ||||
| 		// Find home directory.
 | ||||
| 		home, err := homedir.Dir() | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 		checkErr(err) | ||||
| 		v.AddConfigPath(".") | ||||
| 		v.AddConfigPath(home) | ||||
| 		v.AddConfigPath("/etc/filebrowser/") | ||||
| 		v.SetConfigName(".filebrowser") | ||||
| 	} else { | ||||
| 		// Use config file from the flag.
 | ||||
| 		v.SetConfigFile(cfgFile) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -131,174 +198,4 @@ func initConfig() { | |||
| 	} else { | ||||
| 		log.Println("Using config file:", v.ConfigFileUsed()) | ||||
| 	} | ||||
| 
 | ||||
| 	log.Println("FORCE:", v.GetBool("force")) | ||||
| 
 | ||||
| 	/* | ||||
| 	   if DB exists | ||||
| 	     if force false | ||||
| 	       database has highest priority, if undefined in DB use config params | ||||
| 	     else | ||||
| 	       config params overwrite existing and non-existing params in DB | ||||
| 	   else | ||||
| 	     (quick)Setup with provided config params | ||||
| 	*/ | ||||
| 
 | ||||
| 	/* | ||||
| 	   DISPLAY WARNINGS WHEN DEFAULT VALUES ARE USED | ||||
| 
 | ||||
| 	     This allows to know if a CLI flag was provided: | ||||
| 
 | ||||
| 	       log.Println(rootCmd.Flags().Changed("database")) | ||||
| 
 | ||||
| 	     However, that is not enough in order to know if a value came from a config file or from envvars. | ||||
| 	     This should allow so. But it seems not to work as expected (see spf13/viper#323): | ||||
| 
 | ||||
| 	       log.Println(v.IsSet("database")) | ||||
| 	*/ | ||||
| 
 | ||||
| 	if _, err := os.Stat(v.GetString("database")); os.IsNotExist(err) { | ||||
| 		quickSetup() | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| func serverVisitAndReplace(s *settings.Settings) { | ||||
| 	rootCmd.Flags().Visit(func(flag *pflag.Flag) { | ||||
| 		switch flag.Name { | ||||
| 		case "log": | ||||
| 			s.Log = v.GetString(flag.Name) | ||||
| 		case "address": | ||||
| 			s.Server.Address = v.GetString(flag.Name) | ||||
| 		case "port": | ||||
| 			s.Server.Port = v.GetInt(flag.Name) | ||||
| 		case "cert": | ||||
| 			s.Server.TLSCert = v.GetString(flag.Name) | ||||
| 		case "key": | ||||
| 			s.Server.TLSKey = v.GetString(flag.Name) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| func quickSetup() { | ||||
| 	scope := v.GetString("scope") | ||||
| 	if scope == defaults.scope { | ||||
| 		log.Println("[WARN] Using default value '/srv' as param 'scope'") | ||||
| 	} | ||||
| 
 | ||||
| 	db, err := storm.Open(v.GetString("database")) | ||||
| 	checkErr(err) | ||||
| 	defer db.Close() | ||||
| 
 | ||||
| 	set := &settings.Settings{ | ||||
| 		Key:        generateRandomBytes(64), // 256 bit
 | ||||
| 		BaseURL:    v.GetString("baseurl"), | ||||
| 		Log:        v.GetString("log"), | ||||
| 		Signup:     false, | ||||
| 		AuthMethod: auth.MethodJSONAuth, | ||||
| 		Server: settings.Server{ | ||||
| 			Port:    v.GetInt("port"), | ||||
| 			Address: v.GetString("address"), | ||||
| 			TLSCert: v.GetString("cert"), | ||||
| 			TLSKey:  v.GetString("key"), | ||||
| 		}, | ||||
| 		Defaults: settings.UserDefaults{ | ||||
| 			Scope:  scope, | ||||
| 			Locale: "en", | ||||
| 			Perm: users.Permissions{ | ||||
| 				Admin:    false, | ||||
| 				Execute:  true, | ||||
| 				Create:   true, | ||||
| 				Rename:   true, | ||||
| 				Modify:   true, | ||||
| 				Delete:   true, | ||||
| 				Share:    true, | ||||
| 				Download: true, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	//	serverVisitAndReplace(set)
 | ||||
| 	st := getStorage(db) | ||||
| 
 | ||||
| 	err = st.Settings.Save(set) | ||||
| 	checkErr(err) | ||||
| 
 | ||||
| 	err = st.Auth.Save(&auth.JSONAuth{}) | ||||
| 	checkErr(err) | ||||
| 
 | ||||
| 	password := v.GetString("password") | ||||
| 	if password == "" { | ||||
| 		password, err = users.HashPwd("admin") | ||||
| 		checkErr(err) | ||||
| 	} | ||||
| 
 | ||||
| 	user := &users.User{ | ||||
| 		Username:     v.GetString("admin"), | ||||
| 		Password:     password, | ||||
| 		LockPassword: false, | ||||
| 	} | ||||
| 
 | ||||
| 	set.Defaults.Apply(user) | ||||
| 	user.Perm.Admin = true | ||||
| 
 | ||||
| 	err = st.Users.Save(user) | ||||
| 	checkErr(err) | ||||
| } | ||||
| 
 | ||||
| func setupLogger(s *settings.Settings) { | ||||
| 	switch s.Log { | ||||
| 	case "stdout": | ||||
| 		log.SetOutput(os.Stdout) | ||||
| 	case "stderr": | ||||
| 		log.SetOutput(os.Stderr) | ||||
| 	case "": | ||||
| 		log.SetOutput(ioutil.Discard) | ||||
| 	default: | ||||
| 		log.SetOutput(&lumberjack.Logger{ | ||||
| 			Filename:   s.Log, | ||||
| 			MaxSize:    100, | ||||
| 			MaxAge:     14, | ||||
| 			MaxBackups: 10, | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func startServer(st *storage.Storage) { | ||||
| 	settings, err := st.Settings.Get() | ||||
| 	checkErr(err) | ||||
| 
 | ||||
| 	//	serverVisitAndReplace(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 | ||||
| } | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
							
								
								
									
										28
									
								
								cmd/users.go
								
								
								
								
							
							
						
						
									
										28
									
								
								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") | ||||
| } | ||||
|  | @ -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) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| package cmd | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 
 | ||||
|  | @ -80,3 +81,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 | ||||
| } | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import ( | |||
| 	"encoding/hex" | ||||
| 	"hash" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"mime" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
|  | @ -76,7 +77,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,17 +127,25 @@ func (i *FileInfo) Checksum(algo string) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (i *FileInfo) detectType(modify bool) 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) | ||||
|  | @ -160,17 +169,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 +250,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 | ||||
| 			} | ||||
|  |  | |||
							
								
								
									
										2
									
								
								frontend
								
								
								
								
							
							
								
								
								
								
								
								
							
						
						
									
										2
									
								
								frontend
								
								
								
								
							|  | @ -1 +1 @@ | |||
| Subproject commit 95fc3dfdfbe21b1d55538add66bf0d5f38197320 | ||||
| Subproject commit 0e7d4ef110ee550375d4bf15dfa9ded70214076a | ||||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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 | ||||
| 		} | ||||
|  |  | |||
|  | @ -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) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,23 +9,14 @@ type AuthMethod string | |||
| type Settings struct { | ||||
| 	Key        []byte              `json:"key"` | ||||
| 	BaseURL    string              `json:"baseURL"` | ||||
| 	Log        string              `json:"log"` | ||||
| 	Server     Server              `json:"server"` | ||||
| 	Scope      string              `json:"scope"` | ||||
| 	Signup     bool                `json:"signup"` | ||||
| 	Defaults   UserDefaults        `json:"defaults"` | ||||
| 	AuthMethod AuthMethod          `json:"authMethod"` | ||||
| 	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
 | ||||
| } | ||||
| 
 | ||||
| // Server settings.
 | ||||
| type Server struct { | ||||
| 	Port    int    `json:"port"` | ||||
| 	Address string `json:"address"` | ||||
| 	TLSCert string `json:"tlsCert"` | ||||
| 	TLSKey  string `json:"tlsKey"` | ||||
| 	Rules      []rules.Rule        `json:"rules"` | ||||
| } | ||||
| 
 | ||||
| // GetRules implements rules.Provider.
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
| 		} | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Henrique Dias
						Henrique Dias