FS as an interface, close #205

pull/211/head
Henrique Dias 2017-08-20 09:21:36 +01:00
parent 76de8e5940
commit 20818dca93
No known key found for this signature in database
GPG Key ID: 936F5EB68D786730
8 changed files with 64 additions and 36 deletions

View File

@ -11,7 +11,7 @@ type UsersStore struct {
DB *storm.DB
}
func (u UsersStore) Get(id int) (*fm.User, error) {
func (u UsersStore) Get(id int, builder fm.FSBuilder) (*fm.User, error) {
var us *fm.User
err := u.DB.One("ID", id, us)
if err == storm.ErrNotFound {
@ -22,12 +22,21 @@ func (u UsersStore) Get(id int) (*fm.User, error) {
return nil, err
}
return &fm.User{}, nil
us.FileSystem = builder(us.Scope)
return us, nil
}
func (u UsersStore) Gets() ([]*fm.User, error) {
func (u UsersStore) Gets(builder fm.FSBuilder) ([]*fm.User, error) {
var us []*fm.User
err := u.DB.All(us)
if err != nil {
return us, err
}
for _, user := range us {
user.FileSystem = builder(user.Scope)
}
return us, err
}

View File

@ -79,7 +79,7 @@ func GetInfo(url *url.URL, c *FileManager, u *User) (*File, error) {
i := &File{
URL: "/files" + url.String(),
VirtualPath: url.Path,
Path: filepath.Join(string(u.FileSystem), url.Path),
Path: filepath.Join(u.Scope, url.Path),
}
info, err := u.FileSystem.Stat(url.Path)

View File

@ -89,7 +89,7 @@ var (
// FileManager is a file manager instance. It should be creating using the
// 'New' function and not directly.
type FileManager struct {
// Job cron.
// Cron job to manage schedulings.
Cron *cron.Cron
// The key used to sign the JWT tokens.
@ -98,6 +98,10 @@ type FileManager struct {
// The static assets.
Assets *rice.Box
// The Store is used to manage users, shareable links and
// other stuff that is saved on the database.
Store *Store
// PrefixURL is a part of the URL that is already trimmed from the request URL before it
// arrives to our handlers. It may be useful when using File Manager as a middleware
// such as in caddy-filemanager plugin. It is only useful in certain situations.
@ -121,14 +125,20 @@ type FileManager struct {
// A map of events to a slice of commands.
Commands map[string][]string
Store *Store
// NewFS should build a new file system for a given path.
NewFS FSBuilder
}
// Command is a command function.
type Command func(r *http.Request, m *FileManager, u *User) error
// Load loads the configuration from the database.
func (m *FileManager) Load() error {
// FSBuilder is the File System Builder.
type FSBuilder func(scope string) FileSystem
// Setup loads the configuration from the database and configures
// the Assets and the Cron job. It must always be run after
// creating a File Manager object.
func (m *FileManager) Setup() error {
// Creates a new File Manager instance with the Users
// map and Assets box.
m.Assets = rice.MustFindBox("./assets/dist")
@ -170,7 +180,7 @@ func (m *FileManager) Load() error {
}
// Tries to fetch the users from the database.
users, err := m.Store.Users.Gets()
users, err := m.Store.Users.Gets(m.NewFS)
if err != nil {
return err
}
@ -353,8 +363,11 @@ type User struct {
// Tells if this user is an admin.
Admin bool `json:"admin"`
// Scope is the path the user has access to.
Scope string `json:"filesystem"`
// FileSystem is the virtual file system the user has access.
FileSystem fileutils.Dir `json:"filesystem"`
FileSystem FileSystem `json:"-"`
// Rules is an array of access and deny rules.
Rules []*Rule `json:"rules"`
@ -445,8 +458,8 @@ type Store struct {
// UsersStore is the interface to manage users.
type UsersStore interface {
Get(id int) (*User, error)
Gets() ([]*User, error)
Get(id int, builder FSBuilder) (*User, error)
Gets(builder FSBuilder) ([]*User, error)
Save(u *User) error
Update(u *User, fields ...string) error
Delete(id int) error
@ -479,6 +492,16 @@ type StaticGen interface {
Publish(c *Context, w http.ResponseWriter, r *http.Request) (int, error)
}
// FileSystem is the interface to work with the file system.
type FileSystem interface {
Mkdir(name string, perm os.FileMode) error
OpenFile(name string, flag int, perm os.FileMode) (*os.File, error)
RemoveAll(name string) error
Rename(oldName, newName string) error
Stat(name string) (os.FileInfo, error)
Copy(src, dst string) error
}
// Context contains the needed information to make handlers work.
type Context struct {
*FileManager

View File

@ -30,7 +30,7 @@ func authHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, er
}
// Checks if the user exists.
u, err := c.Store.Users.Get(cred.ID)
u, err := c.Store.Users.Get(cred.ID, c.NewFS)
if err != nil {
return http.StatusForbidden, nil
}
@ -137,7 +137,7 @@ func validateAuth(c *fm.Context, r *http.Request) (bool, *fm.User) {
return false, nil
}
u, err := c.Store.Users.Get(claims.User.ID)
u, err := c.Store.Users.Get(claims.User.ID, c.NewFS)
if err != nil {
return false, nil
}

View File

@ -37,7 +37,7 @@ func resourceHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
return resourceDeleteHandler(c, w, r)
case http.MethodPut:
// Before save command handler.
path := filepath.Join(string(c.User.FileSystem), r.URL.Path)
path := filepath.Join(c.User.Scope, r.URL.Path)
if err := c.Runner("before_save", path); err != nil {
return http.StatusInternalServerError, err
}
@ -253,7 +253,7 @@ func resourcePublishSchedule(c *fm.Context, w http.ResponseWriter, r *http.Reque
}
func resourcePublish(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
path := filepath.Join(string(c.User.FileSystem), r.URL.Path)
path := filepath.Join(c.User.Scope, r.URL.Path)
// Before save command handler.
if err := c.Runner("before_publish", path); err != nil {

View File

@ -28,7 +28,7 @@ func shareHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, e
}
func shareGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
path := filepath.Join(string(c.User.FileSystem), r.URL.Path)
path := filepath.Join(c.User.Scope, r.URL.Path)
s, err := c.Store.Share.GetByPath(path)
if err == storm.ErrNotFound {
return http.StatusNotFound, nil
@ -49,7 +49,7 @@ func shareGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
}
func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
path := filepath.Join(string(c.User.FileSystem), r.URL.Path)
path := filepath.Join(c.User.Scope, r.URL.Path)
var s *fm.ShareLink
expire := r.URL.Query().Get("expires")

View File

@ -64,7 +64,7 @@ func getUserID(r *http.Request) (int, error) {
// getUser returns the user which is present in the request
// body. If the body is empty or the JSON is invalid, it
// returns an fm.Error.
func getUser(r *http.Request) (*fm.User, string, error) {
func getUser(c *fm.Context, r *http.Request) (*fm.User, string, error) {
// Checks if the request body is empty.
if r.Body == nil {
return nil, "", fm.ErrEmptyRequest
@ -82,6 +82,7 @@ func getUser(r *http.Request) (*fm.User, string, error) {
return nil, "", fm.ErrWrongDataType
}
mod.Data.FileSystem = c.NewFS(mod.Data.Scope)
return mod.Data, mod.Which, nil
}
@ -93,7 +94,7 @@ func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
// Request for the listing of users.
if r.URL.Path == "/" {
users, err := c.Store.Users.Gets()
users, err := c.Store.Users.Gets(c.NewFS)
if err != nil {
return http.StatusInternalServerError, err
}
@ -116,7 +117,7 @@ func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
return http.StatusInternalServerError, err
}
u, err := c.Store.Users.Get(id)
u, err := c.Store.Users.Get(id, c.NewFS)
if err == fm.ErrExist {
return http.StatusNotFound, err
}
@ -134,7 +135,7 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
return http.StatusMethodNotAllowed, nil
}
u, _, err := getUser(r)
u, _, err := getUser(c, r)
if err != nil {
return http.StatusBadRequest, err
}
@ -144,8 +145,8 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
return http.StatusBadRequest, fm.ErrEmptyUsername
}
// Checks if filesystem isn't empty.
if u.FileSystem == "" {
// Checks if scope isn't empty.
if u.Scope == "" {
return http.StatusBadRequest, fm.ErrEmptyScope
}
@ -154,11 +155,6 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
return http.StatusBadRequest, fm.ErrEmptyPassword
}
// The username, password and scope cannot be empty.
if u.Username == "" || u.Password == "" || u.FileSystem == "" {
return http.StatusBadRequest, errors.New("username, password or scope is empty")
}
// Initialize rules if they're not initialized.
if u.Rules == nil {
u.Rules = []*fm.Rule{}
@ -175,7 +171,7 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in
}
// Checks if the scope exists.
if code, err := checkFS(string(u.FileSystem)); err != nil {
if code, err := checkFS(u.Scope); err != nil {
return code, err
}
@ -267,7 +263,7 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
}
// Gets the user from the request body.
u, which, err := getUser(r)
u, which, err := getUser(c, r)
if err != nil {
return http.StatusBadRequest, err
}
@ -315,12 +311,12 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
}
// Checks if filesystem isn't empty.
if u.FileSystem == "" {
if u.Scope == "" {
return http.StatusBadRequest, fm.ErrEmptyScope
}
// Checks if the scope exists.
if code, err := checkFS(string(u.FileSystem)); err != nil {
if code, err := checkFS(u.Scope); err != nil {
return code, err
}
@ -335,7 +331,7 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int
}
// Gets the current saved user from the in-memory map.
suser, err := c.Store.Users.Get(id)
suser, err := c.Store.Users.Get(id, c.NewFS)
if err == fm.ErrNotExist {
return http.StatusNotFound, nil
}

View File

@ -82,7 +82,7 @@ func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error)
}
// Gets the path and initializes a buffer.
path := string(c.User.FileSystem) + "/" + r.URL.Path
path := c.User.Scope + "/" + r.URL.Path
path = filepath.Clean(path)
buff := new(bytes.Buffer)
@ -270,7 +270,7 @@ func search(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error)
search = parseSearch(value)
scope := strings.TrimPrefix(r.URL.Path, "/")
scope = "/" + scope
scope = string(c.User.FileSystem) + scope
scope = c.User.Scope + scope
scope = strings.Replace(scope, "\\", "/", -1)
scope = filepath.Clean(scope)