diff --git a/storage/sql/auth.go b/storage/sql/auth.go index eabe1d57..14ddcac8 100644 --- a/storage/sql/auth.go +++ b/storage/sql/auth.go @@ -15,6 +15,7 @@ type authBackend struct { } func (s authBackend) Get(t settings.AuthMethod) (auth.Auther, error) { + logBacktrace() var auther auth.Auther switch t { @@ -30,17 +31,12 @@ func (s authBackend) Get(t settings.AuthMethod) (auth.Auther, error) { fmt.Println("ERROR: unknown auth method " + t) return nil, errors.ErrInvalidAuthMethod } - - val := GetSetting(s.db, "auther") - if val == "" { - return auther, nil - } - return auther, json.Unmarshal([]byte(val), auther) + return auther, nil } func (s authBackend) Save(a auth.Auther) error { val, err := json.Marshal(a) - if !checkError(err, "Fail to save auth.Auther") { + if checkError(err, "Fail to save auth.Auther") { return err } return SetSetting(s.db, "auther", string(val)) diff --git a/storage/sql/settings.go b/storage/sql/settings.go index 09412e92..105b443e 100644 --- a/storage/sql/settings.go +++ b/storage/sql/settings.go @@ -6,12 +6,16 @@ import ( "fmt" "strings" + "github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/users" ) +func init() { +} + type settingsBackend struct { db *sql.DB } @@ -35,7 +39,7 @@ func userDefaultsFromString(s string) settings.UserDefaults { func userDefaultsToString(d settings.UserDefaults) string { data, err := json.Marshal(d) - if !checkError(err, "Fail to stringify settings.UserDefaults") { + if checkError(err, "Fail to stringify settings.UserDefaults") { return "" } return string(data) @@ -53,7 +57,7 @@ func brandingFromString(s string) settings.Branding { func brandingToString(s settings.Branding) string { data, err := json.Marshal(s) - if !checkError(err, "Fail to jsonify settings.Branding") { + if checkError(err, "Fail to jsonify settings.Branding") { return "" } return string(data) @@ -61,27 +65,27 @@ func brandingToString(s settings.Branding) string { func commandsToString(c map[string][]string) string { data, err := json.Marshal(c) - if !checkError(err, "Fail to jsonify commands") { + if checkError(err, "Fail to jsonify commands") { return "" } return string(data) } func commandsFromString(s string) map[string][]string { + c := make(map[string][]string) if s == "" { - return map[string][]string{} + return c } - c := map[string][]string{} err := json.Unmarshal([]byte(s), &c) checkError(err, "Fail to parse commands") return c } func stringsFromString(s string) []string { + c := make([]string, 0) if s == "" { - return []string{} + return c } - c := []string{} err := json.Unmarshal([]byte(s), &c) checkError(err, "Fail to parse []string") return c @@ -89,7 +93,7 @@ func stringsFromString(s string) []string { func stringsToString(c []string) string { data, err := json.Marshal(c) - if !checkError(err, "Fail to jsonify strings") { + if checkError(err, "Fail to jsonify strings") { return "" } return string(data) @@ -126,7 +130,7 @@ func boolToString(b bool) string { func (s settingsBackend) Get() (*settings.Settings, error) { sql := "select key, value from settings" rows, err := s.db.Query(sql) - if !checkError(err, "Fail to Query settings.Settings") { + if checkError(err, "Fail to Query settings.Settings") { return nil, err } key := "" @@ -173,7 +177,7 @@ func (s settingsBackend) Save(ss *settings.Settings) error { RulesToString(ss.Rules)} sql := fmt.Sprintf("INSERT INTO settings (%s) VALUES(%s)", strings.Join(columns, ","), strings.Join(values, ",")) _, err := s.db.Exec(sql) - if !checkError(err, "Fail to insert settings.Settings") { + if checkError(err, "Fail to insert settings.Settings") { return err } return nil @@ -212,11 +216,11 @@ var defaultSettings = settings.Settings{ Share: true, Download: true, }, - Commands: []string{}, + Commands: make([]string, 0), HideDotfiles: false, DateFormat: false, }, - AuthMethod: "json", + AuthMethod: auth.MethodJSONAuth, Branding: settings.Branding{ Name: "", DisableExternal: false, @@ -224,27 +228,28 @@ var defaultSettings = settings.Settings{ Theme: "", Color: "", }, - Commands: map[string][]string{}, - Shell: []string{}, - Rules: []rules.Rule{}, + Commands: make(map[string][]string), + Shell: make([]string, 0), + Rules: make([]rules.Rule, 0), } func cloneServer(server settings.Server) settings.Server { data, err := json.Marshal(server) - if !checkError(err, "Fail to clone settings.Server") { - return settings.Server{} - } s := settings.Server{} - json.Unmarshal(data, &s) + if checkError(err, "Fail to clone settings.Server") { + return s + } + err = json.Unmarshal(data, &s) + checkError(err, "Fail to decode for settings.Server") return s } func cloneSettings(s settings.Settings) settings.Settings { data, err := json.Marshal(s) - if !checkError(err, "Fail to clone settings.Settings") { - return settings.Settings{} - } s1 := settings.Settings{} + if checkError(err, "Fail to clone settings.Settings") { + return s1 + } json.Unmarshal(data, &s1) return s1 } @@ -252,7 +257,7 @@ func cloneSettings(s settings.Settings) settings.Settings { func (s settingsBackend) GetServer() (*settings.Server, error) { sql := "select key, value from settings" rows, err := s.db.Query(sql) - if !checkError(err, "Fail to Query for GetServer") { + if checkError(err, "Fail to Query for GetServer") { return nil, err } server := cloneServer(defaultServer) @@ -261,7 +266,7 @@ func (s settingsBackend) GetServer() (*settings.Server, error) { for rows.Next() { err = rows.Scan(&key, &value) - if !checkError(err, "Fail to query settings.Settings") { + if checkError(err, "Fail to query settings.Settings") { continue } if key == "Root" { @@ -313,7 +318,7 @@ func (s settingsBackend) SaveServer(ss *settings.Server) error { "'" + ss.AuthHook + "'"} sql := fmt.Sprintf("INSERT INTO settings (%s) VALUES(%s)", strings.Join(columns, ","), strings.Join(values, ",")) _, err := s.db.Exec(sql) - if !checkError(err, "Fail to insert for settings.Settings") { + if checkError(err, "Fail to insert for settings.Settings") { return err } return nil @@ -323,7 +328,7 @@ func SetSetting(db *sql.DB, key string, value string) error { sql := "select count(key) from settings where key = '" + key + "'" count := 0 err := db.QueryRow(sql).Scan(&count) - if !checkError(err, "Fail to QueryRow for key="+key) { + if checkError(err, "Fail to QueryRow for key="+key) { return err } if count == 0 { @@ -336,7 +341,7 @@ func GetSetting(db *sql.DB, key string) string { sql := "select value from settings where key = '" + key + "';" value := "" err := db.QueryRow(sql).Scan(&value) - if !checkError(err, "Fail to QueryRow for key "+key) { + if checkError(err, "Fail to QueryRow for key "+key) { return value } return value diff --git a/storage/sql/share.go b/storage/sql/share.go index 899eea17..0ab865b9 100644 --- a/storage/sql/share.go +++ b/storage/sql/share.go @@ -29,7 +29,7 @@ func parseLink(row linkRecord) (*share.Link, error) { passwordhash := "" token := "" err := row.Scan(&path, &hash, &userid, &expire, &passwordhash, &token) - if !checkError(err, "Fail to parse record for share.Link") { + if checkError(err, "Fail to parse record for share.Link") { return nil, err } link := share.Link{} @@ -48,13 +48,13 @@ func queryLinks(db *sql.DB, condition string) ([]*share.Link, error) { sql = sql + " where " + condition } rows, err := db.Query(sql) - if !checkError(err, "Fail to Query links") { + if checkError(err, "Fail to Query links") { return nil, err } var links []*share.Link = []*share.Link{} for rows.Next() { link, err := parseLink(rows) - if !checkError(err, "Fail to parse record for share.Link") { + if checkError(err, "Fail to parse record for share.Link") { continue } links = append(links, link) diff --git a/storage/sql/sql.go b/storage/sql/sql.go index 071ce614..f0698061 100644 --- a/storage/sql/sql.go +++ b/storage/sql/sql.go @@ -3,7 +3,6 @@ package sql import ( "database/sql" "errors" - "fmt" "strings" "github.com/filebrowser/filebrowser/v2/auth" @@ -56,17 +55,19 @@ func NewStorage(db *sql.DB) (*storage.Storage, error) { authStore := auth.NewStorage(authBackend{db: db}, userStore) err := SetSetting(db, "version", "2") - if !checkError(err, "Fail to set version") { + if checkError(err, "Fail to set version") { return nil, err } // TODO: default - if GetSetting(db, "auther") == "" { - err := SetSetting(db, "auther", "json") - if !checkError(err, "Fail to set auther") { - return nil, err + /* + if GetSetting(db, "auther") == "" { + err := SetSetting(db, "auther", "json") + if checkError(err, "Fail to set auther") { + return nil, err + } } - } + */ storage := &storage.Storage{ Auth: authStore, @@ -76,12 +77,3 @@ func NewStorage(db *sql.DB) (*storage.Storage, error) { } return storage, nil } - -func checkError(err error, message string) bool { - if err != nil { - fmt.Println("ERROR: " + err.Error()) - fmt.Println("ERROR: " + message) - return false - } - return true -} diff --git a/storage/sql/users.go b/storage/sql/users.go index 856646e6..93568c93 100644 --- a/storage/sql/users.go +++ b/storage/sql/users.go @@ -4,10 +4,12 @@ import ( "database/sql" "encoding/json" "fmt" + "log" "reflect" "strconv" "strings" + "github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/users" @@ -18,10 +20,10 @@ type usersBackend struct { } func PermFromString(s string) users.Permissions { - if s == "" { - return users.Permissions{} - } var perm users.Permissions + if s == "" { + return perm + } err := json.Unmarshal([]byte(s), &perm) checkError(err, "Fail to parse perm from string") return perm @@ -29,7 +31,7 @@ func PermFromString(s string) users.Permissions { func PermToString(perm users.Permissions) string { data, err := json.Marshal(perm) - if !checkError(err, "Fail to stringify users.Permissions") { + if checkError(err, "Fail to stringify users.Permissions") { return "" } return string(data) @@ -37,7 +39,7 @@ func PermToString(perm users.Permissions) string { func CommandsFromString(s string) []string { if s == "" { - return []string{} + return make([]string, 0) } var commands []string err := json.Unmarshal([]byte(s), &commands) @@ -47,7 +49,7 @@ func CommandsFromString(s string) []string { func CommandsToString(commands []string) string { data, err := json.Marshal(commands) - if !checkError(err, "Fail to stringify users commands") { + if checkError(err, "Fail to stringify users commands") { return "" } return string(data) @@ -65,17 +67,17 @@ func SortingFromString(s string) files.Sorting { func SortingToString(sorting files.Sorting) string { data, err := json.Marshal(sorting) - if !checkError(err, "Fail to stringify files.Sorting") { + if checkError(err, "Fail to stringify files.Sorting") { return "" } return string(data) } func rulesFromString(s string) []rules.Rule { + rules := make([]rules.Rule, 0) if s == "" { - return []rules.Rule{} + return rules } - var rules []rules.Rule err := json.Unmarshal([]byte(s), &rules) checkError(err, "Fail to parse Rules from string") return rules @@ -83,34 +85,82 @@ func rulesFromString(s string) []rules.Rule { func RulesToString(rules []rules.Rule) string { data, err := json.Marshal(rules) - if !checkError(err, "Fail to stringify []rules.Rule") { + if checkError(err, "Fail to stringify []rules.Rule") { return "" } return string(data) } +var adminUser = createAdminUser() + +func createAdminUser() users.User { + userDefault := defaultSettings.Defaults + user := users.User{} + user.Username = "admin" + user.Password = "admin" + user.Scope = userDefault.Scope + user.LockPassword = false + user.ViewMode = userDefault.ViewMode + user.Perm = users.Permissions{ + Admin: true, + Execute: true, + Create: true, + Rename: true, + Modify: true, + Delete: true, + Share: true, + Download: true, + } + user.Commands = userDefault.Commands + user.Sorting = userDefault.Sorting + return user +} + func InitUserTable(db *sql.DB) error { + logBacktrace() sql := "create table if not exists users (id integer primary key, username string, password string, scope string, lockpassword bool, viewmode string, perm string, commands string, sorting string, rules string);" _, err := db.Exec(sql) + if checkError(err, "Fail to create users table") { + return err + } + user, err := usersBackend{db}.Get("admin") + if user == nil { + log.Println("No admin exists") + err := usersBackend{db}.Save(&adminUser) + checkError(err, "Fail to init admin user") + } return err } -func (s usersBackend) Get(id interface{}) (*users.User, error) { - userID := id.(uint) +func (s usersBackend) Get(i interface{}) (*users.User, error) { + columns := []string{"id", "username", "password", "scope", "lockpassword", "viewmode", "perm", "commands", "sorting", "rules"} + columnsStr := strings.Join(columns, ",") + var conditionStr string + switch i.(type) { + case uint: + conditionStr = fmt.Sprintf("id=%v", i) + case int: + conditionStr = fmt.Sprintf("id=%v", i) + case string: + conditionStr = fmt.Sprintf("username='%v'", i) + default: + return nil, errors.ErrInvalidDataType + } + userID := uint(0) username := "" password := "" scope := "" lockpassword := false - var viewmode users.ViewMode = "list" + var viewmode users.ViewMode = users.ListViewMode perm := "" commands := "" sorting := "" rules := "" user := users.User{} - sql := "select username, password, scope, lockpassword, viewmode, perm,commands,sorting,rules from users where id=" + strconv.Itoa(int(userID)) - err := s.db.QueryRow(sql).Scan(&username, &password, &scope, &lockpassword, &viewmode, &perm, &commands, &sorting, &rules) - if !checkError(err, "Fail to QueryRow for user") { - return &user, err + sql := fmt.Sprintf("select %s from users where %s", columnsStr, conditionStr) + err := s.db.QueryRow(sql).Scan(&userID, &username, &password, &scope, &lockpassword, &viewmode, &perm, &commands, &sorting, &rules) + if checkError(err, "") { + return nil, err } user.ID = userID user.Username = username @@ -128,10 +178,10 @@ func (s usersBackend) Get(id interface{}) (*users.User, error) { func (s usersBackend) Gets() ([]*users.User, error) { sql := "select id, username, password, scope, lockpassword, viewmode, perm,commands,sorting,rules from users" rows, err := s.db.Query(sql) - if !checkError(err, "Fail to Query []*users.User") { + if checkError(err, "Fail to Query []*users.User") { return nil, err } - var users2 []*users.User = []*users.User{} + var users2 []*users.User = make([]*users.User, 0) for rows.Next() { id := 0 username := "" @@ -144,7 +194,7 @@ func (s usersBackend) Gets() ([]*users.User, error) { sorting := "" rules := "" err := rows.Scan(&id, &username, &password, &scope, &lockpassword, &viewmode, &perm, &commands, &sorting, &rules) - if !checkError(err, "Fail to parse record for user.User") { + if checkError(err, "Fail to parse record for user.User") { continue } user := users.User{} @@ -191,10 +241,14 @@ func (s usersBackend) updateUser(id uint, user *users.User) error { } func (s usersBackend) insertUser(user *users.User) error { + password, err := users.HashPwd(user.Password) + if checkError(err, "Fail to hash password") { + return err + } sql := fmt.Sprintf( - "insert into users (username, password, scope, lockpassword, viewmode, perm, commands, sorting) values ('%s','%s','%s',%s,'%s','%s','%s','%s','%s')", + "insert into users (username, password, scope, lockpassword, viewmode, perm, commands, sorting, rules) values ('%s','%s','%s',%s,'%s','%s','%s','%s','%s')", user.Username, - user.Password, + password, user.Scope, boolToString(user.LockPassword), user.ViewMode, @@ -203,15 +257,14 @@ func (s usersBackend) insertUser(user *users.User) error { SortingToString(user.Sorting), RulesToString(user.Rules), ) - _, err := s.db.Exec(sql) + _, err = s.db.Exec(sql) + checkError(err, "Fail to insert user") return err } func (s usersBackend) Save(user *users.User) error { - userOriginal, err := s.GetBy(user.ID) - if !checkError(err, "Fail to Save *users.User") { - return err - } + userOriginal, err := s.GetBy(user.Username) + checkError(err, "") if userOriginal != nil { return s.updateUser(user.ID, user) } diff --git a/storage/sql/utils.go b/storage/sql/utils.go new file mode 100644 index 00000000..202155a9 --- /dev/null +++ b/storage/sql/utils.go @@ -0,0 +1,57 @@ +package sql + +import ( + "log" + "path/filepath" + "runtime" + "strings" +) + +func getRuntimeFunctionName(frame uint) string { + pc := make([]uintptr, 1) + count := runtime.Callers(int(frame)+2, pc) + if count == 0 { + return "" + } + f := runtime.FuncForPC(pc[0]) + return f.Name() +} + +func checkError(err error, message string) bool { + if err != nil { + if len(message) > 0 { + funcname := filepath.Base(getRuntimeFunctionName(1)) + log.Printf("ERROR [%s]: %s\n", funcname, err.Error()) + log.Printf("ERROR [%s]: %s\n", funcname, message) + } + return true + } + return false +} + +func logFunction() { + funcname := getRuntimeFunctionName(1) + log.Printf("%s is running\n", funcname) +} + +func reverse(list []string) []string { + var output []string + for i := len(list) - 1; i >= 0; i-- { + output = append(output, list[i]) + } + return output +} + +func logBacktrace() { + funcs := make([]string, 0) + for _, i := range []int{1, 2, 3} { + p := filepath.Base(getRuntimeFunctionName(uint(i))) + if len(p) > 0 { + funcs = append(funcs, p) + } else { + break + } + } + funcs = reverse(funcs) + log.Printf("%s\n", strings.Join(funcs, " -> ")) +}