some cleaning

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
Henrique Dias 2019-01-03 14:06:03 +00:00
parent 94cec714ce
commit f3c1c4b5d2
37 changed files with 492 additions and 376 deletions

View File

@ -18,14 +18,14 @@ type jsonCred struct {
ReCaptcha string `json:"recaptcha"` ReCaptcha string `json:"recaptcha"`
} }
// JSONAuth is a json implementaion of an auther. // JSONAuth is a json implementaion of an Auther.
type JSONAuth struct { type JSONAuth struct {
ReCaptcha *ReCaptcha ReCaptcha *ReCaptcha
Store *types.UsersVerify `json:"-"` store *types.Storage
} }
// Auth authenticates the user via a json in content body. // 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 var cred jsonCred
if r.Body == nil { 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) { if err != nil || !types.CheckPwd(cred.Password, u.Password) {
return nil, types.ErrNoPermission return nil, types.ErrNoPermission
} }
@ -58,6 +58,11 @@ func (a JSONAuth) Auth(r *http.Request) (*types.User, error) {
return u, nil 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" const reCaptchaAPI = "/recaptcha/api/siteverify"
// ReCaptcha identifies a recaptcha conenction. // ReCaptcha identifies a recaptcha conenction.

View File

@ -11,10 +11,15 @@ const MethodNoAuth types.AuthMethod = "noauth"
// NoAuth is no auth implementation of auther. // NoAuth is no auth implementation of auther.
type NoAuth struct { type NoAuth struct {
Store *types.UsersVerify `json:"-"` store *types.Storage
} }
// Auth uses authenticates user 1. // Auth uses authenticates user 1.
func (a NoAuth) Auth(r *http.Request) (*types.User, error) { func (a *NoAuth) Auth(r *http.Request) (*types.User, error) {
return a.Store.Get(1) return a.store.GetUser(1)
}
// SetStorage attaches the storage information to the auther.
func (a *NoAuth) SetStorage(s *types.Storage) {
a.store = s
} }

View File

@ -12,16 +12,21 @@ const MethodProxyAuth types.AuthMethod = "proxy"
// ProxyAuth is a proxy implementation of an auther. // ProxyAuth is a proxy implementation of an auther.
type ProxyAuth struct { type ProxyAuth struct {
Header string Header string
Store *types.UsersVerify `json:"-"` store *types.Storage
} }
// Auth authenticates the user via an HTTP header. // 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) username := r.Header.Get(a.Header)
user, err := a.Store.GetByUsername(username) user, err := a.store.GetUser(username)
if err == types.ErrNotExist { if err == types.ErrNotExist {
return nil, types.ErrNoPermission return nil, types.ErrNoPermission
} }
return user, err return user, err
} }
// SetStorage attaches the storage information to the auther.
func (a *ProxyAuth) SetStorage(s *types.Storage) {
a.store = s
}

View File

@ -2,11 +2,7 @@ package bolt
import "github.com/asdine/storm" import "github.com/asdine/storm"
func Open(path string) (*storm.DB, error) { // Backend implements types.StorageBackend.
db, err := storm.Open(path) type Backend struct {
if err != nil { DB *storm.DB
return nil, err
}
return db, nil
} }

View File

@ -6,15 +6,8 @@ import (
"github.com/filebrowser/filebrowser/types" "github.com/filebrowser/filebrowser/types"
) )
// ConfigStore is a configuration store. func (b Backend) get(name string, to interface{}) error {
type ConfigStore struct { err := b.DB.Get("config", name, to)
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)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
return types.ErrNotExist return types.ErrNotExist
} }
@ -22,53 +15,36 @@ func (c ConfigStore) Get(name string, to interface{}) error {
return err return err
} }
// Save saves a configuration from an interface to the database. func (b Backend) save(name string, from interface{}) error {
func (c ConfigStore) Save(name string, from interface{}) error { return b.DB.Set("config", name, from)
return c.DB.Set("config", name, from)
} }
// GetSettings is an helper method to get a settings object. func (b Backend) GetSettings() (*types.Settings, error) {
func (c ConfigStore) GetSettings() (*types.Settings, error) {
settings := &types.Settings{} 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 (b Backend) SaveSettings(s *types.Settings) error {
func (c ConfigStore) SaveSettings(s *types.Settings) error { return b.save("settings", s)
return c.Save("settings", s)
} }
// GetAuther is an helper method to get an auther object. func (b Backend) GetAuther(t types.AuthMethod) (types.Auther, error) {
func (c ConfigStore) GetAuther(t types.AuthMethod) (types.Auther, error) { var auther types.Auther
if t == auth.MethodJSONAuth {
auther := auth.JSONAuth{} switch t {
if err := c.Get("auther", &auther); err != nil { case auth.MethodJSONAuth:
return nil, err auther = &auth.JSONAuth{}
} case auth.MethodProxyAuth:
auther.Store = &types.UsersVerify{Store: &UsersStore{DB: c.DB}} auther = &auth.ProxyAuth{}
return &auther, nil case auth.MethodNoAuth:
auther = &auth.NoAuth{}
default:
return nil, types.ErrInvalidAuthMethod
} }
if t == auth.MethodProxyAuth { return auther, b.get("auther", auther)
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
} }
// SaveAuther is an helper method to set the auther object func (b Backend) SaveAuther(a types.Auther) error {
func (c ConfigStore) SaveAuther(a types.Auther) error { return b.save("auther", a)
return c.Save("auther", a)
} }

View File

@ -6,13 +6,7 @@ import (
"github.com/filebrowser/filebrowser/types" "github.com/filebrowser/filebrowser/types"
) )
// ShareStore is a shareable links store. func (s Backend) GetLinkByHash(hash string) (*types.ShareLink, error) {
type ShareStore struct {
DB *storm.DB
}
// Get gets a Share Link from an hash.
func (s ShareStore) Get(hash string) (*types.ShareLink, error) {
var v types.ShareLink var v types.ShareLink
err := s.DB.One("Hash", hash, &v) err := s.DB.One("Hash", hash, &v)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
@ -22,8 +16,7 @@ func (s ShareStore) Get(hash string) (*types.ShareLink, error) {
return &v, err return &v, err
} }
// GetPermanent gets the permanent link from a path. func (s Backend) GetLinkPermanent(path string) (*types.ShareLink, error) {
func (s ShareStore) GetPermanent(path string) (*types.ShareLink, error) {
var v types.ShareLink var v types.ShareLink
err := s.DB.Select(q.Eq("Path", path), q.Eq("Expires", false)).First(&v) err := s.DB.Select(q.Eq("Path", path), q.Eq("Expires", false)).First(&v)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
@ -33,8 +26,7 @@ func (s ShareStore) GetPermanent(path string) (*types.ShareLink, error) {
return &v, err return &v, err
} }
// GetByPath gets all the links for a specific path. func (s Backend) GetLinksByPath(hash string) ([]*types.ShareLink, error) {
func (s ShareStore) GetByPath(hash string) ([]*types.ShareLink, error) {
var v []*types.ShareLink var v []*types.ShareLink
err := s.DB.Find("Path", hash, &v) err := s.DB.Find("Path", hash, &v)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
@ -44,23 +36,10 @@ func (s ShareStore) GetByPath(hash string) ([]*types.ShareLink, error) {
return v, err return v, err
} }
// Gets retrieves all the shareable links. func (s Backend) SaveLink(l *types.ShareLink) error {
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 {
return s.DB.Save(l) return s.DB.Save(l)
} }
// Delete deletes a Share Link from the database. func (s Backend) DeleteLink(hash string) error {
func (s ShareStore) Delete(hash string) error {
return s.DB.DeleteStruct(&types.ShareLink{Hash: hash}) return s.DB.DeleteStruct(&types.ShareLink{Hash: hash})
} }

View File

@ -7,11 +7,7 @@ import (
"github.com/filebrowser/filebrowser/types" "github.com/filebrowser/filebrowser/types"
) )
type UsersStore struct { func (st Backend) GetUserByID(id uint) (*types.User, error) {
DB *storm.DB
}
func (st UsersStore) Get(id uint) (*types.User, error) {
user := &types.User{} user := &types.User{}
err := st.DB.One("ID", id, user) err := st.DB.One("ID", id, user)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
@ -25,7 +21,7 @@ func (st UsersStore) Get(id uint) (*types.User, error) {
return user, nil return user, nil
} }
func (st UsersStore) GetByUsername(username string) (*types.User, error) { func (st Backend) GetUserByUsername(username string) (*types.User, error) {
user := &types.User{} user := &types.User{}
err := st.DB.One("Username", username, user) err := st.DB.One("Username", username, user)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
@ -39,7 +35,7 @@ func (st UsersStore) GetByUsername(username string) (*types.User, error) {
return user, nil return user, nil
} }
func (st UsersStore) Gets() ([]*types.User, error) { func (st Backend) GetUsers() ([]*types.User, error) {
users := []*types.User{} users := []*types.User{}
err := st.DB.All(&users) err := st.DB.All(&users)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
@ -53,9 +49,9 @@ func (st UsersStore) Gets() ([]*types.User, error) {
return users, err 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 { if len(fields) == 0 {
return st.Save(user) return st.SaveUser(user)
} }
for _, field := range fields { for _, field := range fields {
@ -68,7 +64,7 @@ func (st UsersStore) Update(user *types.User, fields ...string) error {
return nil return nil
} }
func (st UsersStore) Save(user *types.User) error { func (st Backend) SaveUser(user *types.User) error {
err := st.DB.Save(user) err := st.DB.Save(user)
if err == storm.ErrAlreadyExists { if err == storm.ErrAlreadyExists {
return types.ErrExist return types.ErrExist
@ -76,12 +72,12 @@ func (st UsersStore) Save(user *types.User) error {
return err return err
} }
func (st UsersStore) Delete(id uint) error { func (st Backend) DeleteUserByID(id uint) error {
return st.DB.DeleteStruct(&types.User{ID: id}) return st.DB.DeleteStruct(&types.User{ID: id})
} }
func (st UsersStore) DeleteByUsername(username string) error { func (st Backend) DeleteUserByUsername(username string) error {
user, err := st.GetByUsername(username) user, err := st.GetUserByUsername(username)
if err != nil { if err != nil {
return err return err
} }

View File

@ -21,14 +21,14 @@ var cmdsAddCmd = &cobra.Command{
db := getDB() db := getDB()
defer db.Close() defer db.Close()
st := getStore(db) st := getStore(db)
s, err := st.Config.GetSettings() s, err := st.GetSettings()
checkErr(err) checkErr(err)
evt := mustGetString(cmd, "event") evt := mustGetString(cmd, "event")
command := mustGetString(cmd, "command") command := mustGetString(cmd, "command")
s.Commands[evt] = append(s.Commands[evt], command) s.Commands[evt] = append(s.Commands[evt], command)
err = st.Config.SaveSettings(s) err = st.SaveSettings(s)
checkErr(err) checkErr(err)
printEvents(s.Commands) printEvents(s.Commands)
}, },

View File

@ -18,7 +18,7 @@ var cmdsLsCmd = &cobra.Command{
db := getDB() db := getDB()
defer db.Close() defer db.Close()
st := getStore(db) st := getStore(db)
s, err := st.Config.GetSettings() s, err := st.GetSettings()
checkErr(err) checkErr(err)
evt := mustGetString(cmd, "event") evt := mustGetString(cmd, "event")

View File

@ -21,7 +21,7 @@ var cmdsRmCmd = &cobra.Command{
db := getDB() db := getDB()
defer db.Close() defer db.Close()
st := getStore(db) st := getStore(db)
s, err := st.Config.GetSettings() s, err := st.GetSettings()
checkErr(err) checkErr(err)
evt := mustGetString(cmd, "event") evt := mustGetString(cmd, "event")
@ -29,7 +29,7 @@ var cmdsRmCmd = &cobra.Command{
checkErr(err) checkErr(err)
s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][i+1:]...) s.Commands[evt] = append(s.Commands[evt][:i], s.Commands[evt][i+1:]...)
err = st.Config.SaveSettings(s) err = st.SaveSettings(s)
checkErr(err) checkErr(err)
printEvents(s.Commands) printEvents(s.Commands)
}, },

View File

@ -55,15 +55,15 @@ func getAuthentication(cmd *cobra.Command) (types.AuthMethod, types.Auther) {
if header == "" { if header == "" {
panic(errors.New("you must set the flag 'auth.header' for method 'proxy'")) 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 { if method == auth.MethodNoAuth {
auther = auth.NoAuth{} auther = &auth.NoAuth{}
} }
if method == auth.MethodJSONAuth { if method == auth.MethodJSONAuth {
jsonAuth := auth.JSONAuth{} jsonAuth := &auth.JSONAuth{}
host := mustGetString(cmd, "recaptcha.host") host := mustGetString(cmd, "recaptcha.host")
key := mustGetString(cmd, "recaptcha.key") key := mustGetString(cmd, "recaptcha.key")

View File

@ -17,9 +17,9 @@ var configCatCmd = &cobra.Command{
db := getDB() db := getDB()
defer db.Close() defer db.Close()
st := getStore(db) st := getStore(db)
s, err := st.Config.GetSettings() s, err := st.GetSettings()
checkErr(err) checkErr(err)
auther, err := st.Config.GetAuther(s.AuthMethod) auther, err := st.GetAuther(s.AuthMethod)
checkErr(err) checkErr(err)
printSettings(s, auther) printSettings(s, auther)
}, },

View File

@ -7,7 +7,6 @@ import (
"strings" "strings"
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/bolt"
"github.com/filebrowser/filebrowser/types" "github.com/filebrowser/filebrowser/types"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -51,7 +50,7 @@ override the options.`,
}, },
} }
db, err := bolt.Open(databasePath) db, err := storm.Open(databasePath)
checkErr(err) checkErr(err)
defer db.Close() 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) { func saveConfig(db *storm.DB, s *types.Settings, a types.Auther) {
st := getStore(db) st := getStore(db)
err := st.Config.SaveSettings(s) err := st.SaveSettings(s)
checkErr(err) checkErr(err)
err = st.Config.SaveAuther(a) err = st.SaveAuther(a)
checkErr(err) checkErr(err)
} }

View File

@ -24,7 +24,7 @@ you want to change.`,
defer db.Close() defer db.Close()
st := getStore(db) st := getStore(db)
s, err := st.Config.GetSettings() s, err := st.GetSettings()
checkErr(err) checkErr(err)
auth := false auth := false
@ -52,14 +52,14 @@ you want to change.`,
var auther types.Auther var auther types.Auther
if auth { if auth {
s.AuthMethod, auther = getAuthentication(cmd) s.AuthMethod, auther = getAuthentication(cmd)
err = st.Config.SaveAuther(auther) err = st.SaveAuther(auther)
checkErr(err) checkErr(err)
} else { } else {
auther, err = st.Config.GetAuther(s.AuthMethod) auther, err = st.GetAuther(s.AuthMethod)
checkErr(err) checkErr(err)
} }
err = st.Config.SaveSettings(s) err = st.SaveSettings(s)
checkErr(err) checkErr(err)
printSettings(s, auther) printSettings(s, auther)
}, },

View File

@ -10,8 +10,8 @@ import (
"os" "os"
"strconv" "strconv"
"github.com/asdine/storm"
"github.com/filebrowser/filebrowser/auth" "github.com/filebrowser/filebrowser/auth"
"github.com/filebrowser/filebrowser/bolt"
"github.com/filebrowser/filebrowser/types" "github.com/filebrowser/filebrowser/types"
fhttp "github.com/filebrowser/filebrowser/http" 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), Store: getStore(db),
} }
env.Settings, err = env.Store.Config.GetSettings() env.Settings, err = env.Store.GetSettings()
checkErr(err) checkErr(err)
env.Auther, err = env.Store.Config.GetAuther(env.Settings.AuthMethod) env.Auther, err = env.Store.GetAuther(env.Settings.AuthMethod)
checkErr(err) checkErr(err)
startServer(cmd, env) startServer(cmd, env)
@ -127,14 +127,14 @@ func quickSetup(cmd *cobra.Command) {
user.ApplyDefaults(settings.Defaults) user.ApplyDefaults(settings.Defaults)
user.Perm.Admin = true user.Perm.Admin = true
db, err := bolt.Open(databasePath) db, err := storm.Open(databasePath)
checkErr(err) checkErr(err)
defer db.Close() defer db.Close()
saveConfig(db, settings, &auth.JSONAuth{}) saveConfig(db, settings, &auth.JSONAuth{})
st := getStore(db) st := getStore(db)
err = st.Users.Save(user) err = st.SaveUser(user)
checkErr(err) checkErr(err)
} }

View File

@ -40,11 +40,11 @@ var findUsers = func(cmd *cobra.Command, args []string) {
var user *types.User var user *types.User
if username != "" { if username != "" {
user, err = st.Users.GetByUsername(username) user, err = st.GetUser(username)
} else if id != 0 { } else if id != 0 {
user, err = st.Users.Get(id) user, err = st.GetUser(id)
} else { } else {
users, err = st.Users.Gets() users, err = st.GetUsers()
} }
checkErr(err) checkErr(err)

View File

@ -25,7 +25,7 @@ var usersNewCmd = &cobra.Command{
defer db.Close() defer db.Close()
st := getStore(db) st := getStore(db)
settings, err := st.Config.GetSettings() settings, err := st.GetSettings()
checkErr(err) checkErr(err)
getUserDefaults(cmd, &settings.Defaults, false) getUserDefaults(cmd, &settings.Defaults, false)
@ -41,7 +41,7 @@ var usersNewCmd = &cobra.Command{
user.ApplyDefaults(settings.Defaults) user.ApplyDefaults(settings.Defaults)
err = st.Users.Save(user) err = st.SaveUser(user)
checkErr(err) checkErr(err)
printUsers([]*types.User{user}) printUsers([]*types.User{user})
}, },

View File

@ -28,9 +28,9 @@ var usersRmCmd = &cobra.Command{
var err error var err error
if username != "" { if username != "" {
err = st.Users.DeleteByUsername(username) err = st.DeleteUser(username)
} else { } else {
err = st.Users.Delete(id) err = st.DeleteUser(id)
} }
checkErr(err) checkErr(err)

View File

@ -33,9 +33,9 @@ options you want to change.`,
var err error var err error
if id != 0 { if id != 0 {
user, err = st.Users.Get(id) user, err = st.GetUser(id)
} else { } else {
user, err = st.Users.GetByUsername(username) user, err = st.GetUser(username)
} }
checkErr(err) checkErr(err)
@ -66,7 +66,7 @@ options you want to change.`,
checkErr(err) checkErr(err)
} }
err = st.Users.Update(user) err = st.UpdateUser(user)
checkErr(err) checkErr(err)
printUsers([]*types.User{user}) printUsers([]*types.User{user})
}, },

View File

@ -34,30 +34,13 @@ func getDB() *storm.DB {
panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first.")) panic(errors.New(databasePath + " does not exist. Please run 'filebrowser init' first."))
} }
db, err := bolt.Open(databasePath) db, err := storm.Open(databasePath)
checkErr(err) checkErr(err)
return db return db
} }
func getStore(db *storm.DB) *types.Store { func getStore(db *storm.DB) *types.Storage {
usersStore := &types.UsersVerify{ return types.NewStorage(&bolt.Backend{DB: db})
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 generateRandomBytes(n int) []byte { func generateRandomBytes(n int) []byte {

View File

@ -67,7 +67,7 @@ func (e *Env) signupHandler(w http.ResponseWriter, r *http.Request) {
} }
user.Password = pwd user.Password = pwd
err = e.Store.Users.Save(user) err = e.Store.SaveUser(user)
if err == types.ErrExist { if err == types.ErrExist {
httpErr(w, r, http.StatusConflict, nil) httpErr(w, r, http.StatusConflict, nil)
return return
@ -133,7 +133,7 @@ func (e *Env) auth(next http.HandlerFunc) http.HandlerFunc {
} }
if !tk.VerifyExpiresAt(time.Now().Add(time.Hour).Unix(), true) { 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") w.Header().Add("X-Renew-Token", "true")
} }

View File

@ -8,13 +8,14 @@ import (
"sync" "sync"
"time" "time"
"github.com/gorilla/websocket"
"github.com/filebrowser/filebrowser/types" "github.com/filebrowser/filebrowser/types"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/websocket"
) )
type key int type key int
const ( const (
keyUserID key = iota keyUserID key = iota
) )
@ -28,7 +29,7 @@ type modifyRequest struct {
type Env struct { type Env struct {
Auther types.Auther Auther types.Auther
Settings *types.Settings Settings *types.Settings
Store *types.Store Store *types.Storage
mux sync.RWMutex // settings mutex for Settings changes. 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) { func (e *Env) getUser(w http.ResponseWriter, r *http.Request) (*types.User, bool) {
id := r.Context().Value(keyUserID).(uint) id := r.Context().Value(keyUserID).(uint)
user, err := e.Store.Users.Get(id) user, err := e.Store.GetUser(id)
if err == types.ErrNotExist { if err == types.ErrNotExist {
httpErr(w, r, http.StatusForbidden, nil) httpErr(w, r, http.StatusForbidden, nil)
return nil, false return nil, false

View File

@ -28,9 +28,6 @@ func parseQueryFiles(r *http.Request, f *types.File, u *types.User) ([]string, e
} }
name = fileutils.SlashClean(name) name = fileutils.SlashClean(name)
if !u.IsAllowed(name) {
continue
}
files = append(files, filepath.Join(f.Path, name)) files = append(files, filepath.Join(f.Path, name))
} }
} }
@ -70,7 +67,7 @@ func (e *Env) rawHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
file, err := types.NewFileInfo(user, path) file, err := types.NewFile(user, path)
if err != nil { if err != nil {
httpErr(w, r, httpFsErr(err), err) httpErr(w, r, httpFsErr(err), err)
return return

View File

@ -42,10 +42,10 @@ func (e *Env) getResourceData(w http.ResponseWriter, r *http.Request, prefix str
path = "/" path = "/"
} }
if !user.IsAllowed(path) { /* TODO if !user.IsAllowed(path) {
httpErr(w, r, http.StatusForbidden, nil) httpErr(w, r, http.StatusForbidden, nil)
return "", nil, false return "", nil, false
} } */
return path, user, true return path, user, true
} }
@ -56,7 +56,7 @@ func (e *Env) resourceGetHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
file, err := types.NewFileInfo(user, path) file, err := types.NewFile(user, path)
if err != nil { if err != nil {
httpErr(w, r, httpFsErr(err), err) httpErr(w, r, httpFsErr(err), err)
return return

View File

@ -68,7 +68,7 @@ func (e *Env) settingsPutHandler(w http.ResponseWriter, r *http.Request) {
settings.Shell = req.Shell settings.Shell = req.Shell
settings.Commands = req.Commands settings.Commands = req.Commands
err = e.Store.Config.SaveSettings(settings) err = e.Store.SaveSettings(settings)
if err != nil { if err != nil {
httpErr(w, r, http.StatusInternalServerError, err) httpErr(w, r, http.StatusInternalServerError, err)
return return

View File

@ -34,7 +34,7 @@ func (e *Env) shareGetHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
s, err := e.Store.Share.GetByPath(path) s, err := e.Store.GetLinksByPath(path)
if err == types.ErrNotExist { if err == types.ErrNotExist {
renderJSON(w, r, []*types.ShareLink{}) renderJSON(w, r, []*types.ShareLink{})
return return
@ -47,7 +47,7 @@ func (e *Env) shareGetHandler(w http.ResponseWriter, r *http.Request) {
for i, link := range s { for i, link := range s {
if link.Expires && link.ExpireDate.Before(time.Now()) { 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:]...) s = append(s[:i], s[i+1:]...)
} }
} }
@ -73,7 +73,7 @@ func (e *Env) shareDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err := e.Store.Share.Delete(hash) err := e.Store.DeleteLink(hash)
if err != nil { if err != nil {
httpErr(w, r, http.StatusInternalServerError, err) httpErr(w, r, http.StatusInternalServerError, err)
return return
@ -92,7 +92,7 @@ func (e *Env) sharePostHandler(w http.ResponseWriter, r *http.Request) {
if expire == "" { if expire == "" {
var err error var err error
s, err = e.Store.Share.GetPermanent(path) s, err = e.Store.GetLinkPermanent(path)
if err == nil { if err == nil {
w.Write([]byte(e.Settings.BaseURL + "/share/" + s.Hash)) w.Write([]byte(e.Settings.BaseURL + "/share/" + s.Hash))
return return
@ -136,7 +136,7 @@ func (e *Env) sharePostHandler(w http.ResponseWriter, r *http.Request) {
s.ExpireDate = time.Now().Add(add) 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) httpErr(w, r, http.StatusInternalServerError, err)
return return
} }

View File

@ -57,7 +57,7 @@ func (e *Env) usersGetHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
users, err := e.Store.Users.Gets() users, err := e.Store.GetUsers()
if err != nil { if err != nil {
httpErr(w, r, http.StatusInternalServerError, err) httpErr(w, r, http.StatusInternalServerError, err)
return return
@ -100,7 +100,7 @@ func (e *Env) userGetHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
u, err := e.Store.Users.Get(id) u, err := e.Store.GetUser(id)
if err == types.ErrNotExist { if err == types.ErrNotExist {
httpErr(w, r, http.StatusNotFound, nil) httpErr(w, r, http.StatusNotFound, nil)
return return
@ -121,7 +121,7 @@ func (e *Env) userDeleteHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err := e.Store.Users.Delete(id) err := e.Store.DeleteUser(id)
if err == types.ErrNotExist { if err == types.ErrNotExist {
httpErr(w, r, http.StatusNotFound, nil) httpErr(w, r, http.StatusNotFound, nil)
return return
@ -160,7 +160,7 @@ func (e *Env) userPostHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
err = e.Store.Users.Save(req.Data) err = e.Store.SaveUser(req.Data)
if err != nil { if err != nil {
httpErr(w, r, http.StatusInternalServerError, err) httpErr(w, r, http.StatusInternalServerError, err)
return return
@ -198,7 +198,7 @@ func (e *Env) userPutHandler(w http.ResponseWriter, r *http.Request) {
req.Data.Password, err = types.HashPwd(req.Data.Password) req.Data.Password, err = types.HashPwd(req.Data.Password)
} else { } else {
var suser *types.User var suser *types.User
suser, err = e.Store.Users.Get(modifiedID) suser, err = e.Store.GetUser(modifiedID)
req.Data.Password = suser.Password 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) 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 { if err != nil {
httpErr(w, r, http.StatusInternalServerError, err) httpErr(w, r, http.StatusInternalServerError, err)
} }

View File

@ -139,10 +139,6 @@ func (e *Env) searchHandler(w http.ResponseWriter, r *http.Request) {
scope := strings.TrimPrefix(r.URL.Path, "/api/search") scope := strings.TrimPrefix(r.URL.Path, "/api/search")
err = search.Search(user.Fs, scope, value, func(path string, f os.FileInfo) error { 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{}{ response, _ := json.Marshal(map[string]interface{}{
"dir": f.IsDir(), "dir": f.IsDir(),
"path": path, "path": path,

View File

@ -2,8 +2,10 @@ package types
import "net/http" import "net/http"
// Auther is the interface each authentication method must // Auther is the authentication interface.
// implement.
type Auther 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)
} }

View File

@ -13,4 +13,5 @@ var (
ErrNoPermission = errors.New("permission denied") ErrNoPermission = errors.New("permission denied")
ErrInvalidAuthMethod = errors.New("invalid auth method") ErrInvalidAuthMethod = errors.New("invalid auth method")
ErrEmptyKey = errors.New("empty key") ErrEmptyKey = errors.New("empty key")
ErrInvalidDataType = errors.New("invalid data type")
) )

View File

@ -35,8 +35,8 @@ type File struct {
Checksums map[string]string `json:"checksums,omitempty"` Checksums map[string]string `json:"checksums,omitempty"`
} }
// NewFileInfo generates a new file info from a user and a path. // NewFile generates a new file info from a user and a path.
func NewFileInfo(u *User, path string) (*File, error) { func NewFile(u *User, path string) (*File, error) {
f := &File{ f := &File{
Path: path, Path: path,
} }
@ -120,10 +120,6 @@ func (f *File) getDirInfo() error {
name := i.Name() name := i.Name()
path := filepath.Join(f.Path, name) path := filepath.Join(f.Path, name)
if !f.user.IsAllowed(path) {
continue
}
if strings.HasPrefix(i.Mode().String(), "L") { if strings.HasPrefix(i.Mode().String(), "L") {
// It's a symbolic link. We try to follow it. If it doesn't work, // 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. // we stay with the link information instead if the target's.

139
types/fs.go Normal file
View File

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

View File

@ -16,11 +16,6 @@ type Settings struct {
Rules []Rule `json:"rules"` // TODO: use this add to cli 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. // Sorting contains a sorting order.
type Sorting struct { type Sorting struct {
By string `json:"by"` By string `json:"by"`

View File

@ -1,18 +1,222 @@
package types package types
// Store is used to persist data. import (
type Store struct { "strings"
Users *UsersVerify )
Config *ConfigVerify
Share ShareStore // 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. // Storage implements Storage interface and verifies
type ShareStore interface { // the data before getting in and out the database.
Get(hash string) (*ShareLink, error) type Storage struct {
GetPermanent(path string) (*ShareLink, error) src StorageBackend
GetByPath(path string) ([]*ShareLink, error) }
Gets() ([]*ShareLink, error)
Save(s *ShareLink) error // NewStorage creates a Storage from a StorageBackend.
Delete(hash string) error 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)
} }

View File

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

View File

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

View File

@ -54,7 +54,7 @@ var checkableFields = []string{
"Rules", "Rules",
} }
func (u *User) clean(fields ...string) error { func (u *User) clean(settings *Settings, fields ...string) error {
if len(fields) == 0 { if len(fields) == 0 {
fields = checkableFields fields = checkableFields
} }
@ -93,17 +93,16 @@ func (u *User) clean(fields ...string) error {
} }
if u.Fs == nil { 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 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. // CanExecute checks if an user can execute a specific command.
func (u *User) CanExecute(command string) bool { func (u *User) CanExecute(command string) bool {
if !u.Perm.Execute { if !u.Perm.Execute {