merge with master

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
pull/606/head
Henrique Dias 2019-01-06 13:20:17 +00:00
commit 2b8bd28158
25 changed files with 233 additions and 386 deletions

View File

@ -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)
}

View File

@ -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.

View File

@ -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)
}

View File

@ -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
}

View File

@ -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.

View File

@ -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)

View File

@ -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"),

View File

@ -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)
}
})

View File

@ -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
}

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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
}

View File

@ -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
}

@ -1 +1 @@
Subproject commit 95fc3dfdfbe21b1d55538add66bf0d5f38197320
Subproject commit 0e7d4ef110ee550375d4bf15dfa9ded70214076a

View File

@ -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 {

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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