diff --git a/auth/json.go b/auth/json.go index 696be5b9..d47dafd4 100644 --- a/auth/json.go +++ b/auth/json.go @@ -18,14 +18,14 @@ type jsonCred struct { ReCaptcha string `json:"recaptcha"` } -// JSONAuth is a json implementaion of an auther. +// JSONAuth is a json implementaion of an Auther. type JSONAuth struct { ReCaptcha *ReCaptcha - Store *types.UsersVerify `json:"-"` + store *types.Storage } // Auth authenticates the user via a json in content body. -func (a JSONAuth) Auth(r *http.Request) (*types.User, error) { +func (a *JSONAuth) Auth(r *http.Request) (*types.User, error) { var cred jsonCred if r.Body == nil { @@ -50,7 +50,7 @@ func (a JSONAuth) Auth(r *http.Request) (*types.User, error) { } } - u, err := a.Store.GetByUsername(cred.Username) + u, err := a.store.GetUser(cred.Username) if err != nil || !types.CheckPwd(cred.Password, u.Password) { return nil, types.ErrNoPermission } @@ -58,6 +58,11 @@ func (a JSONAuth) Auth(r *http.Request) (*types.User, error) { return u, nil } +// SetStorage attaches the storage information to the auther. +func (a *JSONAuth) SetStorage(s *types.Storage) { + a.store = s +} + const reCaptchaAPI = "/recaptcha/api/siteverify" // ReCaptcha identifies a recaptcha conenction. diff --git a/auth/none.go b/auth/none.go index 4bf08f89..1bf12e4e 100644 --- a/auth/none.go +++ b/auth/none.go @@ -11,10 +11,15 @@ const MethodNoAuth types.AuthMethod = "noauth" // NoAuth is no auth implementation of auther. type NoAuth struct { - Store *types.UsersVerify `json:"-"` + store *types.Storage } // Auth uses authenticates user 1. -func (a NoAuth) Auth(r *http.Request) (*types.User, error) { - return a.Store.Get(1) +func (a *NoAuth) Auth(r *http.Request) (*types.User, error) { + return a.store.GetUser(1) +} + +// SetStorage attaches the storage information to the auther. +func (a *NoAuth) SetStorage(s *types.Storage) { + a.store = s } diff --git a/auth/proxy.go b/auth/proxy.go index 07c8e4d9..6f3dabea 100644 --- a/auth/proxy.go +++ b/auth/proxy.go @@ -12,16 +12,21 @@ const MethodProxyAuth types.AuthMethod = "proxy" // ProxyAuth is a proxy implementation of an auther. type ProxyAuth struct { Header string - Store *types.UsersVerify `json:"-"` + store *types.Storage } // Auth authenticates the user via an HTTP header. -func (a ProxyAuth) Auth(r *http.Request) (*types.User, error) { +func (a *ProxyAuth) Auth(r *http.Request) (*types.User, error) { username := r.Header.Get(a.Header) - user, err := a.Store.GetByUsername(username) + user, err := a.store.GetUser(username) if err == types.ErrNotExist { return nil, types.ErrNoPermission } return user, err } + +// SetStorage attaches the storage information to the auther. +func (a *ProxyAuth) SetStorage(s *types.Storage) { + a.store = s +} diff --git a/bolt/bolt.go b/bolt/bolt.go index c9815c9d..92be59ee 100644 --- a/bolt/bolt.go +++ b/bolt/bolt.go @@ -2,11 +2,7 @@ package bolt import "github.com/asdine/storm" -func Open(path string) (*storm.DB, error) { - db, err := storm.Open(path) - if err != nil { - return nil, err - } - - return db, nil +// Backend implements types.StorageBackend. +type Backend struct { + DB *storm.DB } diff --git a/bolt/config.go b/bolt/config.go index f1b82599..1ff926dd 100644 --- a/bolt/config.go +++ b/bolt/config.go @@ -6,15 +6,8 @@ import ( "github.com/filebrowser/filebrowser/types" ) -// ConfigStore is a configuration store. -type ConfigStore struct { - DB *storm.DB - Users *types.UsersVerify -} - -// Get gets a configuration from the database to an interface. -func (c ConfigStore) Get(name string, to interface{}) error { - err := c.DB.Get("config", name, to) +func (b Backend) get(name string, to interface{}) error { + err := b.DB.Get("config", name, to) if err == storm.ErrNotFound { return types.ErrNotExist } @@ -22,53 +15,36 @@ func (c ConfigStore) Get(name string, to interface{}) error { return err } -// Save saves a configuration from an interface to the database. -func (c ConfigStore) Save(name string, from interface{}) error { - return c.DB.Set("config", name, from) +func (b Backend) save(name string, from interface{}) error { + return b.DB.Set("config", name, from) } -// GetSettings is an helper method to get a settings object. -func (c ConfigStore) GetSettings() (*types.Settings, error) { +func (b Backend) GetSettings() (*types.Settings, error) { settings := &types.Settings{} - return settings, c.Get("settings", settings) + return settings, b.get("settings", settings) } -// SaveSettings is an helper method to set the settings object -func (c ConfigStore) SaveSettings(s *types.Settings) error { - return c.Save("settings", s) +func (b Backend) SaveSettings(s *types.Settings) error { + return b.save("settings", s) } -// GetAuther is an helper method to get an auther object. -func (c ConfigStore) GetAuther(t types.AuthMethod) (types.Auther, error) { - if t == auth.MethodJSONAuth { - auther := auth.JSONAuth{} - if err := c.Get("auther", &auther); err != nil { - return nil, err - } - auther.Store = &types.UsersVerify{Store: &UsersStore{DB: c.DB}} - return &auther, nil +func (b Backend) GetAuther(t types.AuthMethod) (types.Auther, error) { + var auther types.Auther + + switch t { + case auth.MethodJSONAuth: + auther = &auth.JSONAuth{} + case auth.MethodProxyAuth: + auther = &auth.ProxyAuth{} + case auth.MethodNoAuth: + auther = &auth.NoAuth{} + default: + return nil, types.ErrInvalidAuthMethod } - if t == auth.MethodProxyAuth { - auther := auth.ProxyAuth{} - if err := c.Get("auther", &auther); err != nil { - return nil, err - } - return &auther, nil - } - - if t == auth.MethodNoAuth { - auther := auth.NoAuth{Store: c.Users} - if err := c.Get("auther", &auther); err != nil { - return nil, err - } - return &auther, nil - } - - return nil, types.ErrInvalidAuthMethod + return auther, b.get("auther", auther) } -// SaveAuther is an helper method to set the auther object -func (c ConfigStore) SaveAuther(a types.Auther) error { - return c.Save("auther", a) +func (b Backend) SaveAuther(a types.Auther) error { + return b.save("auther", a) } diff --git a/bolt/share.go b/bolt/share.go index b701858a..62cbe6bc 100644 --- a/bolt/share.go +++ b/bolt/share.go @@ -6,13 +6,7 @@ import ( "github.com/filebrowser/filebrowser/types" ) -// ShareStore is a shareable links store. -type ShareStore struct { - DB *storm.DB -} - -// Get gets a Share Link from an hash. -func (s ShareStore) Get(hash string) (*types.ShareLink, error) { +func (s Backend) GetLinkByHash(hash string) (*types.ShareLink, error) { var v types.ShareLink err := s.DB.One("Hash", hash, &v) if err == storm.ErrNotFound { @@ -22,8 +16,7 @@ func (s ShareStore) Get(hash string) (*types.ShareLink, error) { return &v, err } -// GetPermanent gets the permanent link from a path. -func (s ShareStore) GetPermanent(path string) (*types.ShareLink, error) { +func (s Backend) GetLinkPermanent(path string) (*types.ShareLink, error) { var v types.ShareLink err := s.DB.Select(q.Eq("Path", path), q.Eq("Expires", false)).First(&v) if err == storm.ErrNotFound { @@ -33,8 +26,7 @@ func (s ShareStore) GetPermanent(path string) (*types.ShareLink, error) { return &v, err } -// GetByPath gets all the links for a specific path. -func (s ShareStore) GetByPath(hash string) ([]*types.ShareLink, error) { +func (s Backend) GetLinksByPath(hash string) ([]*types.ShareLink, error) { var v []*types.ShareLink err := s.DB.Find("Path", hash, &v) if err == storm.ErrNotFound { @@ -44,23 +36,10 @@ func (s ShareStore) GetByPath(hash string) ([]*types.ShareLink, error) { return v, err } -// Gets retrieves all the shareable links. -func (s ShareStore) Gets() ([]*types.ShareLink, error) { - var v []*types.ShareLink - err := s.DB.All(&v) - if err == storm.ErrNotFound { - return v, types.ErrNotExist - } - - return v, err -} - -// Save stores a Share Link on the database. -func (s ShareStore) Save(l *types.ShareLink) error { +func (s Backend) SaveLink(l *types.ShareLink) error { return s.DB.Save(l) } -// Delete deletes a Share Link from the database. -func (s ShareStore) Delete(hash string) error { +func (s Backend) DeleteLink(hash string) error { return s.DB.DeleteStruct(&types.ShareLink{Hash: hash}) } diff --git a/bolt/users.go b/bolt/users.go index e3f7a2d6..fe9a2f9c 100644 --- a/bolt/users.go +++ b/bolt/users.go @@ -7,11 +7,7 @@ import ( "github.com/filebrowser/filebrowser/types" ) -type UsersStore struct { - DB *storm.DB -} - -func (st UsersStore) Get(id uint) (*types.User, error) { +func (st Backend) GetUserByID(id uint) (*types.User, error) { user := &types.User{} err := st.DB.One("ID", id, user) if err == storm.ErrNotFound { @@ -25,7 +21,7 @@ func (st UsersStore) Get(id uint) (*types.User, error) { return user, nil } -func (st UsersStore) GetByUsername(username string) (*types.User, error) { +func (st Backend) GetUserByUsername(username string) (*types.User, error) { user := &types.User{} err := st.DB.One("Username", username, user) if err == storm.ErrNotFound { @@ -39,7 +35,7 @@ func (st UsersStore) GetByUsername(username string) (*types.User, error) { return user, nil } -func (st UsersStore) Gets() ([]*types.User, error) { +func (st Backend) GetUsers() ([]*types.User, error) { users := []*types.User{} err := st.DB.All(&users) if err == storm.ErrNotFound { @@ -53,9 +49,9 @@ func (st UsersStore) Gets() ([]*types.User, error) { return users, err } -func (st UsersStore) Update(user *types.User, fields ...string) error { +func (st Backend) UpdateUser(user *types.User, fields ...string) error { if len(fields) == 0 { - return st.Save(user) + return st.SaveUser(user) } for _, field := range fields { @@ -68,7 +64,7 @@ func (st UsersStore) Update(user *types.User, fields ...string) error { return nil } -func (st UsersStore) Save(user *types.User) error { +func (st Backend) SaveUser(user *types.User) error { err := st.DB.Save(user) if err == storm.ErrAlreadyExists { return types.ErrExist @@ -76,12 +72,12 @@ func (st UsersStore) Save(user *types.User) error { return err } -func (st UsersStore) Delete(id uint) error { +func (st Backend) DeleteUserByID(id uint) error { return st.DB.DeleteStruct(&types.User{ID: id}) } -func (st UsersStore) DeleteByUsername(username string) error { - user, err := st.GetByUsername(username) +func (st Backend) DeleteUserByUsername(username string) error { + user, err := st.GetUserByUsername(username) if err != nil { return err } diff --git a/cmd/cmds_add.go b/cmd/cmds_add.go index 06d78618..13518eae 100644 --- a/cmd/cmds_add.go +++ b/cmd/cmds_add.go @@ -21,14 +21,14 @@ var cmdsAddCmd = &cobra.Command{ db := getDB() defer db.Close() st := getStore(db) - s, err := st.Config.GetSettings() + s, err := st.GetSettings() checkErr(err) evt := mustGetString(cmd, "event") command := mustGetString(cmd, "command") s.Commands[evt] = append(s.Commands[evt], command) - err = st.Config.SaveSettings(s) + err = st.SaveSettings(s) checkErr(err) printEvents(s.Commands) }, diff --git a/cmd/cmds_ls.go b/cmd/cmds_ls.go index b8368828..d8199410 100644 --- a/cmd/cmds_ls.go +++ b/cmd/cmds_ls.go @@ -18,7 +18,7 @@ var cmdsLsCmd = &cobra.Command{ db := getDB() defer db.Close() st := getStore(db) - s, err := st.Config.GetSettings() + s, err := st.GetSettings() checkErr(err) evt := mustGetString(cmd, "event") diff --git a/cmd/cmds_rm.go b/cmd/cmds_rm.go index a05dab97..d6dc5046 100644 --- a/cmd/cmds_rm.go +++ b/cmd/cmds_rm.go @@ -21,7 +21,7 @@ var cmdsRmCmd = &cobra.Command{ db := getDB() defer db.Close() st := getStore(db) - s, err := st.Config.GetSettings() + s, err := st.GetSettings() checkErr(err) evt := mustGetString(cmd, "event") @@ -29,7 +29,7 @@ var cmdsRmCmd = &cobra.Command{ checkErr(err) s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][i+1:]...) - err = st.Config.SaveSettings(s) + err = st.SaveSettings(s) checkErr(err) printEvents(s.Commands) }, diff --git a/cmd/config.go b/cmd/config.go index fd13c741..8e10ad84 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -55,15 +55,15 @@ func getAuthentication(cmd *cobra.Command) (types.AuthMethod, types.Auther) { if header == "" { panic(errors.New("you must set the flag 'auth.header' for method 'proxy'")) } - auther = auth.ProxyAuth{Header: header} + auther = &auth.ProxyAuth{Header: header} } if method == auth.MethodNoAuth { - auther = auth.NoAuth{} + auther = &auth.NoAuth{} } if method == auth.MethodJSONAuth { - jsonAuth := auth.JSONAuth{} + jsonAuth := &auth.JSONAuth{} host := mustGetString(cmd, "recaptcha.host") key := mustGetString(cmd, "recaptcha.key") diff --git a/cmd/config_cat.go b/cmd/config_cat.go index 4c8dba3e..0deebfcd 100644 --- a/cmd/config_cat.go +++ b/cmd/config_cat.go @@ -17,9 +17,9 @@ var configCatCmd = &cobra.Command{ db := getDB() defer db.Close() st := getStore(db) - s, err := st.Config.GetSettings() + s, err := st.GetSettings() checkErr(err) - auther, err := st.Config.GetAuther(s.AuthMethod) + auther, err := st.GetAuther(s.AuthMethod) checkErr(err) printSettings(s, auther) }, diff --git a/cmd/config_init.go b/cmd/config_init.go index 0f5be68f..6fc305cf 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/asdine/storm" - "github.com/filebrowser/filebrowser/bolt" "github.com/filebrowser/filebrowser/types" "github.com/spf13/cobra" ) @@ -51,7 +50,7 @@ override the options.`, }, } - db, err := bolt.Open(databasePath) + db, err := storm.Open(databasePath) checkErr(err) defer db.Close() @@ -68,8 +67,8 @@ need to call the main command to boot up the server. func saveConfig(db *storm.DB, s *types.Settings, a types.Auther) { st := getStore(db) - err := st.Config.SaveSettings(s) + err := st.SaveSettings(s) checkErr(err) - err = st.Config.SaveAuther(a) + err = st.SaveAuther(a) checkErr(err) } diff --git a/cmd/config_set.go b/cmd/config_set.go index 488d3a9e..737ac25c 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -24,7 +24,7 @@ you want to change.`, defer db.Close() st := getStore(db) - s, err := st.Config.GetSettings() + s, err := st.GetSettings() checkErr(err) auth := false @@ -52,14 +52,14 @@ you want to change.`, var auther types.Auther if auth { s.AuthMethod, auther = getAuthentication(cmd) - err = st.Config.SaveAuther(auther) + err = st.SaveAuther(auther) checkErr(err) } else { - auther, err = st.Config.GetAuther(s.AuthMethod) + auther, err = st.GetAuther(s.AuthMethod) checkErr(err) } - err = st.Config.SaveSettings(s) + err = st.SaveSettings(s) checkErr(err) printSettings(s, auther) }, diff --git a/cmd/root.go b/cmd/root.go index af361ece..b74447de 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,8 +10,8 @@ import ( "os" "strconv" + "github.com/asdine/storm" "github.com/filebrowser/filebrowser/auth" - "github.com/filebrowser/filebrowser/bolt" "github.com/filebrowser/filebrowser/types" fhttp "github.com/filebrowser/filebrowser/http" @@ -61,9 +61,9 @@ listening on loalhost on a random port. Use the flags to change it.`, Store: getStore(db), } - env.Settings, err = env.Store.Config.GetSettings() + env.Settings, err = env.Store.GetSettings() checkErr(err) - env.Auther, err = env.Store.Config.GetAuther(env.Settings.AuthMethod) + env.Auther, err = env.Store.GetAuther(env.Settings.AuthMethod) checkErr(err) startServer(cmd, env) @@ -127,14 +127,14 @@ func quickSetup(cmd *cobra.Command) { user.ApplyDefaults(settings.Defaults) user.Perm.Admin = true - db, err := bolt.Open(databasePath) + db, err := storm.Open(databasePath) checkErr(err) defer db.Close() saveConfig(db, settings, &auth.JSONAuth{}) st := getStore(db) - err = st.Users.Save(user) + err = st.SaveUser(user) checkErr(err) } diff --git a/cmd/users_find.go b/cmd/users_find.go index 599da296..07597354 100644 --- a/cmd/users_find.go +++ b/cmd/users_find.go @@ -40,11 +40,11 @@ var findUsers = func(cmd *cobra.Command, args []string) { var user *types.User if username != "" { - user, err = st.Users.GetByUsername(username) + user, err = st.GetUser(username) } else if id != 0 { - user, err = st.Users.Get(id) + user, err = st.GetUser(id) } else { - users, err = st.Users.Gets() + users, err = st.GetUsers() } checkErr(err) diff --git a/cmd/users_new.go b/cmd/users_new.go index 1d9f946d..b6ff5898 100644 --- a/cmd/users_new.go +++ b/cmd/users_new.go @@ -25,7 +25,7 @@ var usersNewCmd = &cobra.Command{ defer db.Close() st := getStore(db) - settings, err := st.Config.GetSettings() + settings, err := st.GetSettings() checkErr(err) getUserDefaults(cmd, &settings.Defaults, false) @@ -41,7 +41,7 @@ var usersNewCmd = &cobra.Command{ user.ApplyDefaults(settings.Defaults) - err = st.Users.Save(user) + err = st.SaveUser(user) checkErr(err) printUsers([]*types.User{user}) }, diff --git a/cmd/users_rm.go b/cmd/users_rm.go index 17296b39..ef454ba1 100644 --- a/cmd/users_rm.go +++ b/cmd/users_rm.go @@ -28,9 +28,9 @@ var usersRmCmd = &cobra.Command{ var err error if username != "" { - err = st.Users.DeleteByUsername(username) + err = st.DeleteUser(username) } else { - err = st.Users.Delete(id) + err = st.DeleteUser(id) } checkErr(err) diff --git a/cmd/users_update.go b/cmd/users_update.go index f9576fbb..cebcc787 100644 --- a/cmd/users_update.go +++ b/cmd/users_update.go @@ -33,9 +33,9 @@ options you want to change.`, var err error if id != 0 { - user, err = st.Users.Get(id) + user, err = st.GetUser(id) } else { - user, err = st.Users.GetByUsername(username) + user, err = st.GetUser(username) } checkErr(err) @@ -66,7 +66,7 @@ options you want to change.`, checkErr(err) } - err = st.Users.Update(user) + err = st.UpdateUser(user) checkErr(err) printUsers([]*types.User{user}) }, diff --git a/cmd/utils.go b/cmd/utils.go index f1da22ac..23f515c3 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -34,30 +34,13 @@ func getDB() *storm.DB { panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first.")) } - db, err := bolt.Open(databasePath) + db, err := storm.Open(databasePath) checkErr(err) return db } -func getStore(db *storm.DB) *types.Store { - usersStore := &types.UsersVerify{ - Store: bolt.UsersStore{ - DB: db, - }, - } - - configSore := &types.ConfigVerify{ - Store: bolt.ConfigStore{ - DB: db, - Users: usersStore, - }, - } - - return &types.Store{ - Users: usersStore, - Config: configSore, - Share: bolt.ShareStore{DB: db}, - } +func getStore(db *storm.DB) *types.Storage { + return types.NewStorage(&bolt.Backend{DB: db}) } func generateRandomBytes(n int) []byte { diff --git a/http/auth.go b/http/auth.go index d4987498..8a569e5a 100644 --- a/http/auth.go +++ b/http/auth.go @@ -67,7 +67,7 @@ func (e *Env) signupHandler(w http.ResponseWriter, r *http.Request) { } user.Password = pwd - err = e.Store.Users.Save(user) + err = e.Store.SaveUser(user) if err == types.ErrExist { httpErr(w, r, http.StatusConflict, nil) return @@ -133,7 +133,7 @@ func (e *Env) auth(next http.HandlerFunc) http.HandlerFunc { } if !tk.VerifyExpiresAt(time.Now().Add(time.Hour).Unix(), true) { - // TODO: chek if user info was modified + // TODO: chek if user info was modified use timestap w.Header().Add("X-Renew-Token", "true") } diff --git a/http/http.go b/http/http.go index de9496f7..1be6ee33 100644 --- a/http/http.go +++ b/http/http.go @@ -8,13 +8,14 @@ import ( "sync" "time" - "github.com/gorilla/websocket" "github.com/filebrowser/filebrowser/types" "github.com/gorilla/mux" + "github.com/gorilla/websocket" ) type key int + const ( keyUserID key = iota ) @@ -28,7 +29,7 @@ type modifyRequest struct { type Env struct { Auther types.Auther Settings *types.Settings - Store *types.Store + Store *types.Storage mux sync.RWMutex // settings mutex for Settings changes. } @@ -104,7 +105,7 @@ func renderJSON(w http.ResponseWriter, r *http.Request, data interface{}) { func (e *Env) getUser(w http.ResponseWriter, r *http.Request) (*types.User, bool) { id := r.Context().Value(keyUserID).(uint) - user, err := e.Store.Users.Get(id) + user, err := e.Store.GetUser(id) if err == types.ErrNotExist { httpErr(w, r, http.StatusForbidden, nil) return nil, false diff --git a/http/raw.go b/http/raw.go index a550b5f5..ec1b6f71 100644 --- a/http/raw.go +++ b/http/raw.go @@ -28,9 +28,6 @@ func parseQueryFiles(r *http.Request, f *types.File, u *types.User) ([]string, e } name = fileutils.SlashClean(name) - if !u.IsAllowed(name) { - continue - } files = append(files, filepath.Join(f.Path, name)) } } @@ -70,7 +67,7 @@ func (e *Env) rawHandler(w http.ResponseWriter, r *http.Request) { return } - file, err := types.NewFileInfo(user, path) + file, err := types.NewFile(user, path) if err != nil { httpErr(w, r, httpFsErr(err), err) return diff --git a/http/resource.go b/http/resource.go index 73eadec7..e2959ee9 100644 --- a/http/resource.go +++ b/http/resource.go @@ -42,10 +42,10 @@ func (e *Env) getResourceData(w http.ResponseWriter, r *http.Request, prefix str path = "/" } - if !user.IsAllowed(path) { + /* TODO if !user.IsAllowed(path) { httpErr(w, r, http.StatusForbidden, nil) return "", nil, false - } + } */ return path, user, true } @@ -56,7 +56,7 @@ func (e *Env) resourceGetHandler(w http.ResponseWriter, r *http.Request) { return } - file, err := types.NewFileInfo(user, path) + file, err := types.NewFile(user, path) if err != nil { httpErr(w, r, httpFsErr(err), err) return diff --git a/http/settings.go b/http/settings.go index 5e38c8fe..dcef1721 100644 --- a/http/settings.go +++ b/http/settings.go @@ -68,7 +68,7 @@ func (e *Env) settingsPutHandler(w http.ResponseWriter, r *http.Request) { settings.Shell = req.Shell settings.Commands = req.Commands - err = e.Store.Config.SaveSettings(settings) + err = e.Store.SaveSettings(settings) if err != nil { httpErr(w, r, http.StatusInternalServerError, err) return diff --git a/http/share.go b/http/share.go index eb2c11a3..48201861 100644 --- a/http/share.go +++ b/http/share.go @@ -34,7 +34,7 @@ func (e *Env) shareGetHandler(w http.ResponseWriter, r *http.Request) { return } - s, err := e.Store.Share.GetByPath(path) + s, err := e.Store.GetLinksByPath(path) if err == types.ErrNotExist { renderJSON(w, r, []*types.ShareLink{}) return @@ -47,7 +47,7 @@ func (e *Env) shareGetHandler(w http.ResponseWriter, r *http.Request) { for i, link := range s { if link.Expires && link.ExpireDate.Before(time.Now()) { - e.Store.Share.Delete(link.Hash) + e.Store.DeleteLink(link.Hash) s = append(s[:i], s[i+1:]...) } } @@ -73,7 +73,7 @@ func (e *Env) shareDeleteHandler(w http.ResponseWriter, r *http.Request) { return } - err := e.Store.Share.Delete(hash) + err := e.Store.DeleteLink(hash) if err != nil { httpErr(w, r, http.StatusInternalServerError, err) return @@ -92,7 +92,7 @@ func (e *Env) sharePostHandler(w http.ResponseWriter, r *http.Request) { if expire == "" { var err error - s, err = e.Store.Share.GetPermanent(path) + s, err = e.Store.GetLinkPermanent(path) if err == nil { w.Write([]byte(e.Settings.BaseURL + "/share/" + s.Hash)) return @@ -136,7 +136,7 @@ func (e *Env) sharePostHandler(w http.ResponseWriter, r *http.Request) { s.ExpireDate = time.Now().Add(add) } - if err := e.Store.Share.Save(s); err != nil { + if err := e.Store.SaveLink(s); err != nil { httpErr(w, r, http.StatusInternalServerError, err) return } diff --git a/http/users.go b/http/users.go index 5e20cfd8..54499707 100644 --- a/http/users.go +++ b/http/users.go @@ -57,7 +57,7 @@ func (e *Env) usersGetHandler(w http.ResponseWriter, r *http.Request) { return } - users, err := e.Store.Users.Gets() + users, err := e.Store.GetUsers() if err != nil { httpErr(w, r, http.StatusInternalServerError, err) return @@ -100,7 +100,7 @@ func (e *Env) userGetHandler(w http.ResponseWriter, r *http.Request) { return } - u, err := e.Store.Users.Get(id) + u, err := e.Store.GetUser(id) if err == types.ErrNotExist { httpErr(w, r, http.StatusNotFound, nil) return @@ -121,7 +121,7 @@ func (e *Env) userDeleteHandler(w http.ResponseWriter, r *http.Request) { return } - err := e.Store.Users.Delete(id) + err := e.Store.DeleteUser(id) if err == types.ErrNotExist { httpErr(w, r, http.StatusNotFound, nil) return @@ -160,7 +160,7 @@ func (e *Env) userPostHandler(w http.ResponseWriter, r *http.Request) { return } - err = e.Store.Users.Save(req.Data) + err = e.Store.SaveUser(req.Data) if err != nil { httpErr(w, r, http.StatusInternalServerError, err) return @@ -198,7 +198,7 @@ func (e *Env) userPutHandler(w http.ResponseWriter, r *http.Request) { req.Data.Password, err = types.HashPwd(req.Data.Password) } else { var suser *types.User - suser, err = e.Store.Users.Get(modifiedID) + suser, err = e.Store.GetUser(modifiedID) req.Data.Password = suser.Password } @@ -232,7 +232,7 @@ func (e *Env) userPutHandler(w http.ResponseWriter, r *http.Request) { req.Which[k] = strings.Title(v) } - err = e.Store.Users.Update(req.Data, req.Which...) + err = e.Store.UpdateUser(req.Data, req.Which...) if err != nil { httpErr(w, r, http.StatusInternalServerError, err) } diff --git a/http/websockets.go b/http/websockets.go index 46bbe3fc..2a3f85ed 100644 --- a/http/websockets.go +++ b/http/websockets.go @@ -139,10 +139,6 @@ func (e *Env) searchHandler(w http.ResponseWriter, r *http.Request) { scope := strings.TrimPrefix(r.URL.Path, "/api/search") err = search.Search(user.Fs, scope, value, func(path string, f os.FileInfo) error { - if !user.IsAllowed(path) { - return nil - } - response, _ := json.Marshal(map[string]interface{}{ "dir": f.IsDir(), "path": path, diff --git a/types/auth.go b/types/auth.go index 95f7e202..34c40559 100644 --- a/types/auth.go +++ b/types/auth.go @@ -2,8 +2,10 @@ package types import "net/http" -// Auther is the interface each authentication method must -// implement. +// Auther is the authentication interface. type Auther interface { - Auth(r *http.Request) (*User, error) + // Auth is called to authenticate a request. + Auth(*http.Request) (*User, error) + // SetStorage gives the Auther the storage. + SetStorage(*Storage) } diff --git a/types/errors.go b/types/errors.go index 9bf64303..f45dbb3f 100644 --- a/types/errors.go +++ b/types/errors.go @@ -13,4 +13,5 @@ var ( ErrNoPermission = errors.New("permission denied") ErrInvalidAuthMethod = errors.New("invalid auth method") ErrEmptyKey = errors.New("empty key") + ErrInvalidDataType = errors.New("invalid data type") ) diff --git a/types/files.go b/types/files.go index 5dd6d879..e4c47668 100644 --- a/types/files.go +++ b/types/files.go @@ -35,8 +35,8 @@ type File struct { Checksums map[string]string `json:"checksums,omitempty"` } -// NewFileInfo generates a new file info from a user and a path. -func NewFileInfo(u *User, path string) (*File, error) { +// NewFile generates a new file info from a user and a path. +func NewFile(u *User, path string) (*File, error) { f := &File{ Path: path, } @@ -120,10 +120,6 @@ func (f *File) getDirInfo() error { name := i.Name() path := filepath.Join(f.Path, name) - if !f.user.IsAllowed(path) { - continue - } - if strings.HasPrefix(i.Mode().String(), "L") { // It's a symbolic link. We try to follow it. If it doesn't work, // we stay with the link information instead if the target's. diff --git a/types/fs.go b/types/fs.go new file mode 100644 index 00000000..165bfbe8 --- /dev/null +++ b/types/fs.go @@ -0,0 +1,139 @@ +package types + +import ( + "os" + "syscall" + "time" + + "github.com/spf13/afero" +) + +type userFs struct { + source afero.Fs + user *User + settings *Settings +} + +func (u *userFs) isAllowed(name string) bool { + if !isAllowed(name, u.user.Rules) { + return false + } + + return isAllowed(name, u.settings.Rules) +} + +func (u *userFs) Chtimes(name string, a, m time.Time) error { + if !u.isAllowed(name) { + return syscall.ENOENT + } + + return u.source.Chtimes(name, a, m) +} + +func (u *userFs) Chmod(name string, mode os.FileMode) error { + if !u.isAllowed(name) { + return syscall.ENOENT + } + + return u.source.Chmod(name, mode) +} + +func (u *userFs) Name() string { + return "userFs" +} + +func (u *userFs) Stat(name string) (os.FileInfo, error) { + if !u.isAllowed(name) { + return nil, syscall.ENOENT + } + + return u.source.Stat(name) +} + +func (u *userFs) Rename(oldname, newname string) error { + if !u.user.Perm.Rename { + return os.ErrPermission + } + + if !u.isAllowed(oldname) || !u.isAllowed(newname) { + return syscall.ENOENT + } + + return u.source.Rename(oldname, newname) +} + +func (u *userFs) RemoveAll(name string) error { + if !u.user.Perm.Delete { + return os.ErrPermission + } + + if !u.isAllowed(name) { + return syscall.ENOENT + } + + return u.source.RemoveAll(name) +} + +func (u *userFs) Remove(name string) error { + if !u.user.Perm.Delete { + return os.ErrPermission + } + + if !u.isAllowed(name) { + return syscall.ENOENT + } + + return u.source.Remove(name) +} + +func (u *userFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) { + if !u.isAllowed(name) { + return nil, syscall.ENOENT + } + + return u.source.OpenFile(name, flag, perm) +} + +func (u *userFs) Open(name string) (afero.File, error) { + if !u.isAllowed(name) { + return nil, syscall.ENOENT + } + + return u.source.Open(name) +} + +func (u *userFs) Mkdir(name string, perm os.FileMode) error { + if !u.user.Perm.Create { + return os.ErrPermission + } + + if !u.isAllowed(name) { + return syscall.ENOENT + } + + return u.source.Mkdir(name, perm) +} + +func (u *userFs) MkdirAll(name string, perm os.FileMode) error { + if !u.user.Perm.Create { + return os.ErrPermission + } + + if !u.isAllowed(name) { + return syscall.ENOENT + } + + return u.source.MkdirAll(name, perm) +} + +func (u *userFs) Create(name string) (afero.File, error) { + if !u.user.Perm.Create { + return nil, os.ErrPermission + } + + if !u.isAllowed(name) { + return nil, syscall.ENOENT + } + + return u.source.Create(name) +} diff --git a/types/settings.go b/types/settings.go index 2bcb49cc..b382fcd4 100644 --- a/types/settings.go +++ b/types/settings.go @@ -16,11 +16,6 @@ type Settings struct { Rules []Rule `json:"rules"` // TODO: use this add to cli } -// IsAllowed matches the rules against the url. -func (e Settings) IsAllowed(url string) bool { - return isAllowed(url, e.Rules) -} - // Sorting contains a sorting order. type Sorting struct { By string `json:"by"` diff --git a/types/storage.go b/types/storage.go index 411918f7..40e42c4b 100644 --- a/types/storage.go +++ b/types/storage.go @@ -1,18 +1,222 @@ package types -// Store is used to persist data. -type Store struct { - Users *UsersVerify - Config *ConfigVerify - Share ShareStore +import ( + "strings" +) + +// StorageBackend is the interface used to persist data. +type StorageBackend interface { + GetUserByID(uint) (*User, error) + GetUserByUsername(string) (*User, error) + GetUsers() ([]*User, error) + SaveUser(u *User) error + UpdateUser(u *User, fields ...string) error + DeleteUserByID(uint) error + DeleteUserByUsername(string) error + GetSettings() (*Settings, error) + SaveSettings(*Settings) error + GetAuther(AuthMethod) (Auther, error) + SaveAuther(Auther) error + GetLinkByHash(hash string) (*ShareLink, error) + GetLinkPermanent(path string) (*ShareLink, error) + GetLinksByPath(path string) ([]*ShareLink, error) + SaveLink(s *ShareLink) error + DeleteLink(hash string) error } -// ShareStore is the interface to manage share links. -type ShareStore interface { - Get(hash string) (*ShareLink, error) - GetPermanent(path string) (*ShareLink, error) - GetByPath(path string) ([]*ShareLink, error) - Gets() ([]*ShareLink, error) - Save(s *ShareLink) error - Delete(hash string) error +// Storage implements Storage interface and verifies +// the data before getting in and out the database. +type Storage struct { + src StorageBackend +} + +// NewStorage creates a Storage from a StorageBackend. +func NewStorage(src StorageBackend) *Storage { + return &Storage{src: src} +} + +// GetUser 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 (v *Storage) GetUser(id interface{}) (*User, error) { + var ( + user *User + err error + ) + + switch id.(type) { + case string: + user, err = v.src.GetUserByUsername(id.(string)) + case uint: + user, err = v.src.GetUserByID(id.(uint)) + default: + return nil, ErrInvalidDataType + } + + if err != nil { + return nil, err + } + + settings, err := v.GetSettings() + if err != nil { + return nil, err + } + + user.clean(settings) + return user, err +} + +// GetUsers gets a list of all users. +func (v *Storage) GetUsers() ([]*User, error) { + users, err := v.src.GetUsers() + if err != nil { + return nil, err + } + + settings, err := v.GetSettings() + if err != nil { + return nil, err + } + + for _, user := range users { + user.clean(settings) + } + + return users, err +} + +// UpdateUser updates a user in the database. +func (v *Storage) UpdateUser(user *User, fields ...string) error { + settings, err := v.GetSettings() + if err != nil { + return err + } + + err = user.clean(settings, fields...) + if err != nil { + return err + } + + return v.src.UpdateUser(user, fields...) +} + +// SaveUser saves the user in a storage. +func (v *Storage) SaveUser(user *User) error { + settings, err := v.GetSettings() + if err != nil { + return err + } + + if err := user.clean(settings); err != nil { + return err + } + + return v.src.SaveUser(user) +} + +// DeleteUser allows you to delete 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 (v *Storage) DeleteUser(id interface{}) (err error) { + switch id.(type) { + case string: + err = v.src.DeleteUserByUsername(id.(string)) + case uint: + err = v.src.DeleteUserByID(id.(uint)) + default: + err = ErrInvalidDataType + } + + return +} + +// GetSettings wraps a ConfigStore.GetSettings +func (v *Storage) GetSettings() (*Settings, error) { + return v.src.GetSettings() +} + +// SaveSettings wraps a ConfigStore.SaveSettings +func (v *Storage) SaveSettings(s *Settings) error { + s.BaseURL = strings.TrimSuffix(s.BaseURL, "/") + + if len(s.Key) == 0 { + return ErrEmptyKey + } + + if s.Defaults.Locale == "" { + s.Defaults.Locale = "en" + } + + if s.Defaults.Commands == nil { + s.Defaults.Commands = []string{} + } + + if s.Defaults.ViewMode == "" { + s.Defaults.ViewMode = MosaicViewMode + } + + if s.Rules == nil { + s.Rules = []Rule{} + } + + if s.Shell == nil { + s.Shell = []string{} + } + + if s.Commands == nil { + s.Commands = map[string][]string{} + } + + for _, event := range defaultEvents { + if _, ok := s.Commands["before_"+event]; !ok { + s.Commands["before_"+event] = []string{} + } + + if _, ok := s.Commands["after_"+event]; !ok { + s.Commands["after_"+event] = []string{} + } + } + + return v.src.SaveSettings(s) +} + +// GetAuther wraps a ConfigStore.GetAuther +func (v *Storage) GetAuther(t AuthMethod) (Auther, error) { + auther, err := v.src.GetAuther(t) + if err != nil { + return nil, err + } + + auther.SetStorage(v) + return auther, nil +} + +// SaveAuther wraps a ConfigStore.SaveAuther +func (v *Storage) SaveAuther(a Auther) error { + return v.src.SaveAuther(a) +} + +// GetLinkByHash wraps a Storage.GetLinkByHash. +func (v *Storage) GetLinkByHash(hash string) (*ShareLink, error) { + return v.src.GetLinkByHash(hash) +} + +// GetLinkPermanent wraps a Storage.GetLinkPermanent +func (v *Storage) GetLinkPermanent(path string) (*ShareLink, error) { + return v.src.GetLinkPermanent(path) +} + +// GetLinksByPath wraps a Storage.GetLinksByPath +func (v *Storage) GetLinksByPath(path string) ([]*ShareLink, error) { + return v.src.GetLinksByPath(path) +} + +// SaveLink wraps a Storage.SaveLink +func (v *Storage) SaveLink(s *ShareLink) error { + return v.src.SaveLink(s) +} + +// DeleteLink wraps a Storage.DeleteLink +func (v *Storage) DeleteLink(hash string) error { + return v.src.DeleteLink(hash) } diff --git a/types/storage_config.go b/types/storage_config.go deleted file mode 100644 index 77b17540..00000000 --- a/types/storage_config.go +++ /dev/null @@ -1,76 +0,0 @@ -package types - -import "strings" - -// ConfigStore is used to manage configurations relativey to a data storage. -type ConfigStore interface { - GetSettings() (*Settings, error) - SaveSettings(*Settings) error - GetAuther(AuthMethod) (Auther, error) - SaveAuther(Auther) error -} - -// ConfigVerify wraps a ConfigStore and makes the verifications needed. -type ConfigVerify struct { - Store ConfigStore -} - -// GetSettings wraps a ConfigStore.GetSettings -func (v ConfigVerify) GetSettings() (*Settings, error) { - return v.Store.GetSettings() -} - -// SaveSettings wraps a ConfigStore.SaveSettings -func (v ConfigVerify) SaveSettings(s *Settings) error { - s.BaseURL = strings.TrimSuffix(s.BaseURL, "/") - - if len(s.Key) == 0 { - return ErrEmptyKey - } - - if s.Defaults.Locale == "" { - s.Defaults.Locale = "en" - } - - if s.Defaults.Commands == nil { - s.Defaults.Commands = []string{} - } - - if s.Defaults.ViewMode == "" { - s.Defaults.ViewMode = MosaicViewMode - } - - if s.Rules == nil { - s.Rules = []Rule{} - } - - if s.Shell == nil { - s.Shell = []string{} - } - - if s.Commands == nil { - s.Commands = map[string][]string{} - } - - for _, event := range defaultEvents { - if _, ok := s.Commands["before_"+event]; !ok { - s.Commands["before_"+event] = []string{} - } - - if _, ok := s.Commands["after_"+event]; !ok { - s.Commands["after_"+event] = []string{} - } - } - - return v.Store.SaveSettings(s) -} - -// GetAuther wraps a ConfigStore.GetAuther -func (v ConfigVerify) GetAuther(t AuthMethod) (Auther, error) { - return v.Store.GetAuther(t) -} - -// SaveAuther wraps a ConfigStore.SaveAuther -func (v ConfigVerify) SaveAuther(a Auther) error { - return v.Store.SaveAuther(a) -} diff --git a/types/storage_users.go b/types/storage_users.go deleted file mode 100644 index b3ecd81a..00000000 --- a/types/storage_users.go +++ /dev/null @@ -1,82 +0,0 @@ -package types - -// UsersStore is used to manage users relativey to a data storage. -type UsersStore interface { - Get(id uint) (*User, error) - GetByUsername(username string) (*User, error) - Gets() ([]*User, error) - Save(u *User) error - Update(u *User, fields ...string) error - Delete(id uint) error - DeleteByUsername(username string) error -} - -// UsersVerify wraps a UsersStore and makes the verifications needed. -type UsersVerify struct { - Store UsersStore -} - -// Get wraps a UsersStore.Get to verify if everything is right. -func (v UsersVerify) Get(id uint) (*User, error) { - user, err := v.Store.Get(id) - if err != nil { - return nil, err - } - - user.clean() - return user, nil -} - -// GetByUsername wraps a UsersStore.GetByUsername to verify if everything is right. -func (v UsersVerify) GetByUsername(username string) (*User, error) { - user, err := v.Store.GetByUsername(username) - if err != nil { - return nil, err - } - - user.clean() - return user, nil -} - -// Gets wraps a UsersStore.Gets to verify if everything is right. -func (v UsersVerify) Gets() ([]*User, error) { - users, err := v.Store.Gets() - if err != nil { - return nil, err - } - - for _, user := range users { - user.clean() - } - - return users, err -} - -// Update wraps a UsersStore.Update to verify if everything is right. -func (v UsersVerify) Update(user *User, fields ...string) error { - err := user.clean(fields...) - if err != nil { - return err - } - - return v.Store.Update(user, fields...) -} - -// Save wraps a UsersStore.Save to verify if everything is right. -func (v UsersVerify) Save(user *User) error { - if err := user.clean(); err != nil { - return err - } - - return v.Store.Save(user) -} - -// Delete wraps a UsersStore.Delete to verify if everything is right. -func (v UsersVerify) Delete(id uint) error { - return v.Store.Delete(id) -} - -// DeleteByUsername wraps a UsersStore.DeleteByUsername to verify if everything is right. -func (v UsersVerify) DeleteByUsername(username string) error { - return v.Store.DeleteByUsername(username) -} diff --git a/types/user.go b/types/user.go index b822fc52..3c9864aa 100644 --- a/types/user.go +++ b/types/user.go @@ -54,7 +54,7 @@ var checkableFields = []string{ "Rules", } -func (u *User) clean(fields ...string) error { +func (u *User) clean(settings *Settings, fields ...string) error { if len(fields) == 0 { fields = checkableFields } @@ -93,17 +93,16 @@ func (u *User) clean(fields ...string) error { } if u.Fs == nil { - u.Fs = afero.NewBasePathFs(afero.NewOsFs(), u.Scope) + u.Fs = &userFs{ + user: u, + settings: settings, + source: afero.NewBasePathFs(afero.NewOsFs(), u.Scope), + } } return nil } -// IsAllowed checks if an user is allowed to go to a certain path. -func (u *User) IsAllowed(url string) bool { - return isAllowed(url, u.Rules) -} - // CanExecute checks if an user can execute a specific command. func (u *User) CanExecute(command string) bool { if !u.Perm.Execute {