DB Updates :)
Former-commit-id: e9795cfca39aab57f4a4b604c65633958ff22e46 [formerly 7dd93e46eb6915a387dc64500a3fe7f6f955643b] [formerly ffa277f1605e46bb8c914464b1223fe029d579d8 [formerly a04ff87bf9271d0e4eabbbe096ce0704d6cf61ea]] Former-commit-id: ffa472fd3b1534f64a1c343864564bbc0290714b [formerly 7074f824d7d7dd3cd74b884c9f3e96834f662394] Former-commit-id: 4b1e0324de6065fd4daec25d463d3756588b92d8
This commit is contained in:
		
							parent
							
								
									4b602be5e3
								
							
						
					
					
						commit
						764289e52f
					
				|  | @ -22,7 +22,7 @@ | |||
|   <!-- Add to home screen for Windows --> | ||||
|   <meta name="msapplication-TileImage" content="{{ .BaseURL }}/static/img/icons/msapplication-icon-144x144.png"> | ||||
|   <meta name="msapplication-TileColor" content="#2979ff"> | ||||
|   <% for (var chunk of webpack.compilation.chunks) { | ||||
|   <% for (var chunk of webpack.chunks) { | ||||
|     for (var file of chunk.files) { | ||||
|       if (file.match(/\.(js|css)$/)) { %> | ||||
|       <link rel="preload" href="{{ .BaseURL }}/<%= file %>" as="<%= file.match(/\.css$/)?'style':'script' %>"><% }}} %> | ||||
|  |  | |||
|  | @ -0,0 +1,17 @@ | |||
| package bolt | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/asdine/storm" | ||||
| ) | ||||
| 
 | ||||
| type ConfigStore struct { | ||||
| 	DB *storm.DB | ||||
| } | ||||
| 
 | ||||
| func (c ConfigStore) Get(name string, to interface{}) error { | ||||
| 	return c.DB.Get("config", name, to) | ||||
| } | ||||
| 
 | ||||
| func (c ConfigStore) Save(name string, from interface{}) error { | ||||
| 	return c.DB.Set("config", name, from) | ||||
| } | ||||
|  | @ -0,0 +1,36 @@ | |||
| package bolt | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/asdine/storm" | ||||
| 	fm "github.com/hacdias/filemanager" | ||||
| ) | ||||
| 
 | ||||
| type ShareStore struct { | ||||
| 	DB *storm.DB | ||||
| } | ||||
| 
 | ||||
| func (s ShareStore) Get(hash string) (*fm.ShareLink, error) { | ||||
| 	var v *fm.ShareLink | ||||
| 	err := s.DB.One("Hash", hash, &v) | ||||
| 	return v, err | ||||
| } | ||||
| 
 | ||||
| func (s ShareStore) GetByPath(hash string) ([]*fm.ShareLink, error) { | ||||
| 	var v []*fm.ShareLink | ||||
| 	err := s.DB.Find("Path", hash, &v) | ||||
| 	return v, err | ||||
| } | ||||
| 
 | ||||
| func (s ShareStore) Gets(hash string) ([]*fm.ShareLink, error) { | ||||
| 	var v []*fm.ShareLink | ||||
| 	err := s.DB.All(&v) | ||||
| 	return v, err | ||||
| } | ||||
| 
 | ||||
| func (s ShareStore) Save(l *fm.ShareLink) error { | ||||
| 	return s.DB.Save(l) | ||||
| } | ||||
| 
 | ||||
| func (s ShareStore) Delete(hash string) error { | ||||
| 	return s.DB.DeleteStruct(&fm.ShareLink{Hash: hash}) | ||||
| } | ||||
|  | @ -0,0 +1,55 @@ | |||
| package bolt | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 
 | ||||
| 	"github.com/asdine/storm" | ||||
| 	fm "github.com/hacdias/filemanager" | ||||
| ) | ||||
| 
 | ||||
| type UsersStore struct { | ||||
| 	DB *storm.DB | ||||
| } | ||||
| 
 | ||||
| func (u UsersStore) Get(id int) (*fm.User, error) { | ||||
| 	var us *fm.User | ||||
| 	err := u.DB.One("ID", id, us) | ||||
| 	if err == storm.ErrNotFound { | ||||
| 		return nil, fm.ErrUserNotExist | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &fm.User{}, nil | ||||
| } | ||||
| 
 | ||||
| func (u UsersStore) Gets() ([]*fm.User, error) { | ||||
| 	var us []*fm.User | ||||
| 	err := u.DB.All(us) | ||||
| 	return us, err | ||||
| } | ||||
| 
 | ||||
| func (u UsersStore) Update(us *fm.User, fields ...string) error { | ||||
| 	if len(fields) == 0 { | ||||
| 		return u.Save(us) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, field := range fields { | ||||
| 		val := reflect.ValueOf(us).Elem().FieldByName(field).Interface() | ||||
| 		if err := u.DB.UpdateField(us, field, val); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (u UsersStore) Save(us *fm.User) error { | ||||
| 	return u.DB.Save(us) | ||||
| } | ||||
| 
 | ||||
| func (u UsersStore) Delete(id int) error { | ||||
| 	return u.DB.DeleteStruct(&fm.User{ID: id}) | ||||
| } | ||||
							
								
								
									
										28
									
								
								file.go
								
								
								
								
							
							
						
						
									
										28
									
								
								file.go
								
								
								
								
							|  | @ -24,12 +24,12 @@ import ( | |||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	errInvalidOption = errors.New("Invalid option") | ||||
| 	ErrInvalidOption = errors.New("Invalid option") | ||||
| ) | ||||
| 
 | ||||
| // File contains the information about a particular file or directory.
 | ||||
| type File struct { | ||||
| 	// Indicates the Kind of view on the front-end (listing, editor or preview).
 | ||||
| 	// Indicates the Kind of view on the front-end (Listing, editor or preview).
 | ||||
| 	Kind string `json:"kind"` | ||||
| 	// The name of the file.
 | ||||
| 	Name string `json:"name"` | ||||
|  | @ -54,19 +54,19 @@ type File struct { | |||
| 	// Stores the content of a text file.
 | ||||
| 	Content string `json:"content,omitempty"` | ||||
| 
 | ||||
| 	*listing `json:",omitempty"` | ||||
| 	*Listing `json:",omitempty"` | ||||
| 
 | ||||
| 	Metadata string `json:"metadata,omitempty"` | ||||
| 	Language string `json:"language,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // A listing is the context used to fill out a template.
 | ||||
| type listing struct { | ||||
| // A Listing is the context used to fill out a template.
 | ||||
| type Listing struct { | ||||
| 	// The items (files and folders) in the path.
 | ||||
| 	Items []*File `json:"items"` | ||||
| 	// The number of directories in the listing.
 | ||||
| 	// The number of directories in the Listing.
 | ||||
| 	NumDirs int `json:"numDirs"` | ||||
| 	// The number of files (items that aren't directories) in the listing.
 | ||||
| 	// The number of files (items that aren't directories) in the Listing.
 | ||||
| 	NumFiles int `json:"numFiles"` | ||||
| 	// Which sorting order is used.
 | ||||
| 	Sort string `json:"sort"` | ||||
|  | @ -166,7 +166,7 @@ func (i *File) GetListing(u *User, r *http.Request) error { | |||
| 		fileinfos = append(fileinfos, i) | ||||
| 	} | ||||
| 
 | ||||
| 	i.listing = &listing{ | ||||
| 	i.Listing = &Listing{ | ||||
| 		Items:    fileinfos, | ||||
| 		NumDirs:  dirCount, | ||||
| 		NumFiles: fileCount, | ||||
|  | @ -304,7 +304,7 @@ func (i File) Checksum(algo string) (string, error) { | |||
| 	case "sha512": | ||||
| 		h = sha512.New() | ||||
| 	default: | ||||
| 		return "", errInvalidOption | ||||
| 		return "", ErrInvalidOption | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = io.Copy(h, file) | ||||
|  | @ -321,7 +321,7 @@ func (i File) CanBeEdited() bool { | |||
| } | ||||
| 
 | ||||
| // ApplySort applies the sort order using .Order and .Sort
 | ||||
| func (l listing) ApplySort() { | ||||
| func (l Listing) ApplySort() { | ||||
| 	// Check '.Order' to know how to sort
 | ||||
| 	if l.Order == "desc" { | ||||
| 		switch l.Sort { | ||||
|  | @ -350,10 +350,10 @@ func (l listing) ApplySort() { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Implement sorting for listing
 | ||||
| type byName listing | ||||
| type bySize listing | ||||
| type byModified listing | ||||
| // Implement sorting for Listing
 | ||||
| type byName Listing | ||||
| type bySize Listing | ||||
| type byModified Listing | ||||
| 
 | ||||
| // By Name
 | ||||
| func (l byName) Len() int { | ||||
|  |  | |||
							
								
								
									
										128
									
								
								filemanager.go
								
								
								
								
							
							
						
						
									
										128
									
								
								filemanager.go
								
								
								
								
							|  | @ -61,6 +61,7 @@ import ( | |||
| 	"os/exec" | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	rice "github.com/GeertJohan/go.rice" | ||||
| 	"github.com/asdine/storm" | ||||
|  | @ -71,17 +72,14 @@ import ( | |||
| // FileManager is a file manager instance. It should be creating using the
 | ||||
| // 'New' function and not directly.
 | ||||
| type FileManager struct { | ||||
| 	// The BoltDB database for this instance.
 | ||||
| 	db *storm.DB | ||||
| 	// Job cron.
 | ||||
| 	Cron *cron.Cron | ||||
| 
 | ||||
| 	// The key used to sign the JWT tokens.
 | ||||
| 	key []byte | ||||
| 	Key []byte | ||||
| 
 | ||||
| 	// The static assets.
 | ||||
| 	assets *rice.Box | ||||
| 
 | ||||
| 	// Job cron.
 | ||||
| 	cron *cron.Cron | ||||
| 	Assets *rice.Box | ||||
| 
 | ||||
| 	// 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
 | ||||
|  | @ -103,66 +101,42 @@ type FileManager struct { | |||
| 	// The Default User needed to build the New User page.
 | ||||
| 	DefaultUser *User | ||||
| 
 | ||||
| 	// Users is a map with the different configurations for each user.
 | ||||
| 	Users map[string]*User | ||||
| 
 | ||||
| 	// A map of events to a slice of commands.
 | ||||
| 	Commands map[string][]string | ||||
| 
 | ||||
| 	Store *Store | ||||
| } | ||||
| 
 | ||||
| type Store struct { | ||||
| 	Users *UsersStore | ||||
| } | ||||
| 
 | ||||
| // Command is a command function.
 | ||||
| type Command func(r *http.Request, m *FileManager, u *User) error | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| // New creates a new File Manager instance. If 'database' file already
 | ||||
| // exists, it will load the users from there. Otherwise, a new user
 | ||||
| // will be created using the 'base' variable. The 'base' User should
 | ||||
| // not have the Password field hashed.
 | ||||
| func New(database string, base User) (*FileManager, error) { | ||||
| func (m *FileManager) Load() error { | ||||
| 	// Creates a new File Manager instance with the Users
 | ||||
| 	// map and Assets box.
 | ||||
| 	m := &FileManager{ | ||||
| 		Users:  map[string]*User{}, | ||||
| 		cron:   cron.New(), | ||||
| 		assets: rice.MustFindBox("./assets/dist"), | ||||
| 	} | ||||
| 
 | ||||
| 	// Tries to open a database on the location provided. This
 | ||||
| 	// function will automatically create a new one if it doesn't
 | ||||
| 	// exist.
 | ||||
| 	db, err := storm.Open(database) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	m.Assets = rice.MustFindBox("./assets/dist") | ||||
| 	m.Cron = cron.New() | ||||
| 
 | ||||
| 	// Tries to get the encryption key from the database.
 | ||||
| 	// If it doesn't exist, create a new one of 256 bits.
 | ||||
| 	err = db.Get("config", "key", &m.key) | ||||
| 	if err != nil && err == storm.ErrNotFound { | ||||
| 	err := m.Store.Config.Get("key", &m.Key) | ||||
| 	if err != nil && err == ErrNotExist { | ||||
| 		var bytes []byte | ||||
| 		bytes, err = generateRandomBytes(64) | ||||
| 		bytes, err = GenerateRandomBytes(64) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		m.key = bytes | ||||
| 		err = db.Set("config", "key", m.key) | ||||
| 		m.Key = bytes | ||||
| 		err = m.Store.Config.Save("key", m.Key) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Tries to get the event commands from the database.
 | ||||
| 	// If they don't exist, initialize them.
 | ||||
| 	err = db.Get("config", "commands", &m.Commands) | ||||
| 	err = m.Store.Config.Get("commands", &m.Commands) | ||||
| 	if err != nil && err == storm.ErrNotFound { | ||||
| 		m.Commands = map[string][]string{ | ||||
| 			"before_save":    {}, | ||||
|  | @ -170,35 +144,29 @@ func New(database string, base User) (*FileManager, error) { | |||
| 			"before_publish": {}, | ||||
| 			"after_publish":  {}, | ||||
| 		} | ||||
| 		err = db.Set("config", "commands", m.Commands) | ||||
| 		err = m.Store.Config.Save("commands", m.Commands) | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Tries to fetch the users from the database and if there are
 | ||||
| 	// any, add them to the current File Manager instance.
 | ||||
| 	var users []User | ||||
| 	err = db.All(&users) | ||||
| 	// Tries to fetch the users from the database.
 | ||||
| 	users, err := m.Store.Users.Gets() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	for i := range users { | ||||
| 		m.Users[users[i].Username] = &users[i] | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// If there are no users in the database, it creates a new one
 | ||||
| 	// based on 'base' User that must be provided by the function caller.
 | ||||
| 	if len(users) == 0 { | ||||
| 		u := base | ||||
| 		u := *m.DefaultUser | ||||
| 		u.Username = "admin" | ||||
| 
 | ||||
| 		// Hashes the password.
 | ||||
| 		u.Password, err = hashPassword("admin") | ||||
| 		u.Password, err = HashPassword("admin") | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		// The first user must be an administrator.
 | ||||
|  | @ -209,26 +177,19 @@ func New(database string, base User) (*FileManager, error) { | |||
| 		u.AllowPublish = true | ||||
| 
 | ||||
| 		// Saves the user to the database.
 | ||||
| 		if err := db.Save(&u); err != nil { | ||||
| 			return nil, err | ||||
| 		if err := m.Store.Users.Save(&u); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		m.Users[u.Username] = &u | ||||
| 	} | ||||
| 
 | ||||
| 	// Attaches db to this File Manager instance.
 | ||||
| 	m.db = db | ||||
| 	m.DefaultUser.Username = "" | ||||
| 	m.DefaultUser.Password = "" | ||||
| 
 | ||||
| 	// Create the default user, making a copy of the base.
 | ||||
| 	base.Username = "" | ||||
| 	base.Password = "" | ||||
| 	m.DefaultUser = &base | ||||
| 	m.Cron.AddFunc("@hourly", m.ShareCleaner) | ||||
| 	m.Cron.Start() | ||||
| 
 | ||||
| 	m.cron.AddFunc("@hourly", m.shareCleaner) | ||||
| 	m.cron.Start() | ||||
| 
 | ||||
| 	return m, nil | ||||
| } */ | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // RootURL returns the actual URL where
 | ||||
| // File Manager interface can be accessed.
 | ||||
|  | @ -291,24 +252,19 @@ func (m *FileManager) Attach(s StaticGen) error { | |||
| 
 | ||||
| 	m.StaticGen = s | ||||
| 
 | ||||
| 	// TODO: Save...
 | ||||
| 	/* 	err := m.db.Get("staticgen", "hugo", h) | ||||
| 	if err != nil && err == storm.ErrNotFound { | ||||
| 		err = m.db.Set("staticgen", "hugo", *h) | ||||
| 	err = m.Store.Config.Get("staticgen_"+s.Name(), s) | ||||
| 	if err == ErrNotExist { | ||||
| 		return m.Store.Config.Save("staticgen_"+s.Name(), s) | ||||
| 	} | ||||
| 	*/ | ||||
| 	return nil | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| 
 | ||||
| // shareCleaner removes sharing links that are no longer active.
 | ||||
| // ShareCleaner removes sharing links that are no longer active.
 | ||||
| // This function is set to run periodically.
 | ||||
| func (m FileManager) shareCleaner() { | ||||
| 	var links []shareLink | ||||
| 
 | ||||
| func (m FileManager) ShareCleaner() { | ||||
| 	// Get all links.
 | ||||
| 	err := m.db.All(&links) | ||||
| 	links, err := m.Store.Share.Gets() | ||||
| 	if err != nil { | ||||
| 		log.Print(err) | ||||
| 		return | ||||
|  | @ -317,13 +273,13 @@ func (m FileManager) shareCleaner() { | |||
| 	// Find the expired ones.
 | ||||
| 	for i := range links { | ||||
| 		if links[i].Expires && links[i].ExpireDate.Before(time.Now()) { | ||||
| 			err = m.db.DeleteStruct(&links[i]) | ||||
| 			err = m.Store.Share.Delete(links[i].Hash) | ||||
| 			if err != nil { | ||||
| 				log.Print(err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } */ | ||||
| } | ||||
| 
 | ||||
| // Runner runs the commands for a certain event type.
 | ||||
| func (m FileManager) Runner(event string, path string) error { | ||||
|  |  | |||
							
								
								
									
										44
									
								
								http/auth.go
								
								
								
								
							
							
						
						
									
										44
									
								
								http/auth.go
								
								
								
								
							|  | @ -1,14 +1,11 @@ | |||
| package http | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"encoding/json" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| 
 | ||||
| 	jwt "github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/dgrijalva/jwt-go/request" | ||||
| 	fm "github.com/hacdias/filemanager" | ||||
|  | @ -33,13 +30,13 @@ func authHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, er | |||
| 	} | ||||
| 
 | ||||
| 	// Checks if the user exists.
 | ||||
| 	u, ok := c.Users[cred.Username] | ||||
| 	if !ok { | ||||
| 	u, err := c.Store.Users.Get(cred.ID) | ||||
| 	if err != nil { | ||||
| 		return http.StatusForbidden, nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Checks if the password is correct.
 | ||||
| 	if !checkPasswordHash(cred.Password, u.Password) { | ||||
| 	if !fm.CheckPasswordHash(cred.Password, u.Password) { | ||||
| 		return http.StatusForbidden, nil | ||||
| 	} | ||||
| 
 | ||||
|  | @ -86,7 +83,7 @@ func printToken(c *fm.Context, w http.ResponseWriter) (int, error) { | |||
| 
 | ||||
| 	// Creates the token and signs it.
 | ||||
| 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | ||||
| 	signed, err := token.SignedString(c.key) | ||||
| 	signed, err := token.SignedString(c.Key) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
|  | @ -127,7 +124,7 @@ func validateAuth(c *fm.Context, r *http.Request) (bool, *fm.User) { | |||
| 	} | ||||
| 
 | ||||
| 	keyFunc := func(token *jwt.Token) (interface{}, error) { | ||||
| 		return c.key, nil | ||||
| 		return c.Key, nil | ||||
| 	} | ||||
| 	var claims claims | ||||
| 	token, err := request.ParseFromRequestWithClaims(r, | ||||
|  | @ -140,38 +137,11 @@ func validateAuth(c *fm.Context, r *http.Request) (bool, *fm.User) { | |||
| 		return false, nil | ||||
| 	} | ||||
| 
 | ||||
| 	u, ok := c.Users[claims.User.Username] | ||||
| 	if !ok { | ||||
| 	u, err := c.Store.Users.Get(claims.User.ID) | ||||
| 	if err != nil { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 
 | ||||
| 	c.User = u | ||||
| 	return true, u | ||||
| } | ||||
| 
 | ||||
| // hashPassword generates an hash from a password using bcrypt.
 | ||||
| func hashPassword(password string) (string, error) { | ||||
| 	bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) | ||||
| 	return string(bytes), err | ||||
| } | ||||
| 
 | ||||
| // checkPasswordHash compares a password with an hash to check if they match.
 | ||||
| func checkPasswordHash(password, hash string) bool { | ||||
| 	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) | ||||
| 	return err == nil | ||||
| } | ||||
| 
 | ||||
| // generateRandomBytes returns securely generated random bytes.
 | ||||
| // It will return an error if the system's secure random
 | ||||
| // number generator fails to function correctly, in which
 | ||||
| // case the caller should not continue.
 | ||||
| func generateRandomBytes(n int) ([]byte, error) { | ||||
| 	b := make([]byte, n) | ||||
| 	_, err := rand.Read(b) | ||||
| 	// Note that err == nil only if we read len(b) bytes.
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return b, nil | ||||
| } | ||||
|  |  | |||
|  | @ -45,7 +45,7 @@ func TestRenewHandler(t *testing.T) { | |||
| 	// First, we have to make an auth request to get the user authenticated, | ||||
| 	r, err := http.NewRequest("POST", "/api/auth/get", strings.NewReader(defaultCredentials)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 		t.Fatal(fm.Err) | ||||
| 	} | ||||
| 
 | ||||
| 	w := httptest.NewRecorder() | ||||
|  | @ -60,7 +60,7 @@ func TestRenewHandler(t *testing.T) { | |||
| 	// Test renew authorization via Authorization Header. | ||||
| 	r, err = http.NewRequest("GET", "/api/auth/renew", nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 		t.Fatal(fm.Err) | ||||
| 	} | ||||
| 
 | ||||
| 	r.Header.Set("Authorization", "Bearer "+token) | ||||
|  | @ -74,7 +74,7 @@ func TestRenewHandler(t *testing.T) { | |||
| 	// Test renew authorization via cookie field. | ||||
| 	r, err = http.NewRequest("GET", "/api/auth/renew", nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 		t.Fatal(fm.Err) | ||||
| 	} | ||||
| 
 | ||||
| 	r.AddCookie(&http.Cookie{ | ||||
							
								
								
									
										47
									
								
								http/http.go
								
								
								
								
							
							
						
						
									
										47
									
								
								http/http.go
								
								
								
								
							|  | @ -2,7 +2,6 @@ package http | |||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
|  | @ -13,21 +12,10 @@ import ( | |||
| 	fm "github.com/hacdias/filemanager" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	errUserExist          = errors.New("user already exists") | ||||
| 	errUserNotExist       = errors.New("user does not exist") | ||||
| 	errEmptyRequest       = errors.New("request body is empty") | ||||
| 	errEmptyPassword      = errors.New("password is empty") | ||||
| 	errEmptyUsername      = errors.New("username is empty") | ||||
| 	errEmptyScope         = errors.New("scope is empty") | ||||
| 	errWrongDataType      = errors.New("wrong data type") | ||||
| 	errInvalidUpdateField = errors.New("invalid field to update") | ||||
| ) | ||||
| 
 | ||||
| // ServeHTTP is the main entry point of this HTML application.
 | ||||
| func ServeHTTP(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	// Checks if the URL contains the baseURL and strips it. Otherwise, it just
 | ||||
| 	// returns a 404 error because we're not supposed to be here!
 | ||||
| 	// returns a 404 fm.Error because we're not supposed to be here!
 | ||||
| 	p := strings.TrimPrefix(r.URL.Path, c.BaseURL) | ||||
| 
 | ||||
| 	if len(p) >= len(r.URL.Path) && c.BaseURL != "" { | ||||
|  | @ -41,7 +29,7 @@ func ServeHTTP(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro | |||
| 	if r.URL.Path == "/sw.js" { | ||||
| 		return renderFile( | ||||
| 			c, w, | ||||
| 			c.assets.MustString("sw.js"), | ||||
| 			c.Assets.MustString("sw.js"), | ||||
| 			"application/javascript", | ||||
| 		) | ||||
| 	} | ||||
|  | @ -83,7 +71,7 @@ func ServeHTTP(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro | |||
| 
 | ||||
| 	return renderFile( | ||||
| 		c, w, | ||||
| 		c.assets.MustString("index.html"), | ||||
| 		c.Assets.MustString("index.html"), | ||||
| 		"text/html", | ||||
| 	) | ||||
| } | ||||
|  | @ -91,13 +79,13 @@ func ServeHTTP(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro | |||
| // staticHandler handles the static assets path.
 | ||||
| func staticHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	if r.URL.Path != "/static/manifest.json" { | ||||
| 		http.FileServer(c.assets.HTTPBox()).ServeHTTP(w, r) | ||||
| 		http.FileServer(c.Assets.HTTPBox()).ServeHTTP(w, r) | ||||
| 		return 0, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return renderFile( | ||||
| 		c, w, | ||||
| 		c.assets.MustString("static/manifest.json"), | ||||
| 		c.Assets.MustString("static/manifest.json"), | ||||
| 		"application/json", | ||||
| 	) | ||||
| } | ||||
|  | @ -141,7 +129,7 @@ func apiHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, err | |||
| 		var err error | ||||
| 		c.File, err = fm.GetInfo(r.URL, c.FileManager, c.User) | ||||
| 		if err != nil { | ||||
| 			return errorToHTTP(err, false), err | ||||
| 			return ErrorToHTTP(err, false), err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -177,7 +165,7 @@ func checksumHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 	query := r.URL.Query().Get("algo") | ||||
| 
 | ||||
| 	val, err := c.File.Checksum(query) | ||||
| 	if err == errInvalidOption { | ||||
| 	if err == fm.ErrInvalidOption { | ||||
| 		return http.StatusBadRequest, err | ||||
| 	} else if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
|  | @ -211,7 +199,7 @@ func renderFile(c *fm.Context, w http.ResponseWriter, file string, contentType s | |||
| 
 | ||||
| 	err := tpl.Execute(w, map[string]interface{}{ | ||||
| 		"BaseURL":   c.RootURL(), | ||||
| 		"StaticGen": c.staticgen, | ||||
| 		"StaticGen": c.StaticGen.Name(), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
|  | @ -221,12 +209,11 @@ func renderFile(c *fm.Context, w http.ResponseWriter, file string, contentType s | |||
| } | ||||
| 
 | ||||
| func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	var s shareLink | ||||
| 	err := c.db.One("Hash", r.URL.Path, &s) | ||||
| 	s, err := c.Store.Share.Get(r.URL.Path) | ||||
| 	if err == storm.ErrNotFound { | ||||
| 		return renderFile( | ||||
| 			c, w, | ||||
| 			c.assets.MustString("static/share/404.html"), | ||||
| 			c.Assets.MustString("static/share/404.html"), | ||||
| 			"text/html", | ||||
| 		) | ||||
| 	} | ||||
|  | @ -236,10 +223,10 @@ func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro | |||
| 	} | ||||
| 
 | ||||
| 	if s.Expires && s.ExpireDate.Before(time.Now()) { | ||||
| 		c.db.DeleteStruct(&s) | ||||
| 		c.Store.Share.Delete(s.Hash) | ||||
| 		return renderFile( | ||||
| 			c, w, | ||||
| 			c.assets.MustString("static/share/404.html"), | ||||
| 			c.Assets.MustString("static/share/404.html"), | ||||
| 			"text/html", | ||||
| 		) | ||||
| 	} | ||||
|  | @ -248,10 +235,10 @@ func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro | |||
| 
 | ||||
| 	info, err := os.Stat(s.Path) | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, false), err | ||||
| 		return ErrorToHTTP(err, false), err | ||||
| 	} | ||||
| 
 | ||||
| 	c.File = &file{ | ||||
| 	c.File = &fm.File{ | ||||
| 		Path:    s.Path, | ||||
| 		Name:    info.Name(), | ||||
| 		ModTime: info.ModTime(), | ||||
|  | @ -263,7 +250,7 @@ func sharePage(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, erro | |||
| 	dl := r.URL.Query().Get("dl") | ||||
| 
 | ||||
| 	if dl == "" || dl == "0" { | ||||
| 		tpl := template.Must(template.New("file").Parse(c.assets.MustString("static/share/index.html"))) | ||||
| 		tpl := template.Must(template.New("file").Parse(c.Assets.MustString("static/share/index.html"))) | ||||
| 		w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 
 | ||||
| 		err := tpl.Execute(w, map[string]interface{}{ | ||||
|  | @ -303,8 +290,8 @@ func matchURL(first, second string) bool { | |||
| 	return strings.HasPrefix(first, second) | ||||
| } | ||||
| 
 | ||||
| // errorToHTTP converts errors to HTTP Status Code.
 | ||||
| func errorToHTTP(err error, gone bool) int { | ||||
| // ErrorToHTTP converts errors to HTTP Status Code.
 | ||||
| func ErrorToHTTP(err error, gone bool) int { | ||||
| 	switch { | ||||
| 	case err == nil: | ||||
| 		return http.StatusOK | ||||
|  |  | |||
|  | @ -64,9 +64,9 @@ func resourceHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 
 | ||||
| func resourceGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	// Gets the information of the directory/file.
 | ||||
| 	f, err := getInfo(r.URL, c.FileManager, c.User) | ||||
| 	f, err := fm.GetInfo(r.URL, c.FileManager, c.User) | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, false), err | ||||
| 		return ErrorToHTTP(err, false), err | ||||
| 	} | ||||
| 
 | ||||
| 	// If it's a dir and the path doesn't end with a trailing slash,
 | ||||
|  | @ -83,7 +83,7 @@ func resourceGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) ( | |||
| 
 | ||||
| 	// Tries to get the file type.
 | ||||
| 	if err = f.GetFileType(true); err != nil { | ||||
| 		return errorToHTTP(err, true), err | ||||
| 		return ErrorToHTTP(err, true), err | ||||
| 	} | ||||
| 
 | ||||
| 	// Serve a preview if the file can't be edited or the
 | ||||
|  | @ -97,7 +97,7 @@ func resourceGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) ( | |||
| 	f.Kind = "editor" | ||||
| 
 | ||||
| 	// Tries to get the editor data.
 | ||||
| 	if err = f.getEditor(); err != nil { | ||||
| 	if err = f.GetEditor(); err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
|  | @ -109,11 +109,11 @@ func listingHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, | |||
| 	f.Kind = "listing" | ||||
| 
 | ||||
| 	// Tries to get the listing data.
 | ||||
| 	if err := f.getListing(c, r); err != nil { | ||||
| 		return errorToHTTP(err, true), err | ||||
| 	if err := f.GetListing(c.User, r); err != nil { | ||||
| 		return ErrorToHTTP(err, true), err | ||||
| 	} | ||||
| 
 | ||||
| 	listing := f.listing | ||||
| 	listing := f.Listing | ||||
| 
 | ||||
| 	// Defines the cookie scope.
 | ||||
| 	cookieScope := c.RootURL() | ||||
|  | @ -144,7 +144,7 @@ func resourceDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request | |||
| 	// Remove the file or folder.
 | ||||
| 	err := c.User.FileSystem.RemoveAll(r.URL.Path) | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, true), err | ||||
| 		return ErrorToHTTP(err, true), err | ||||
| 	} | ||||
| 
 | ||||
| 	return http.StatusOK, nil | ||||
|  | @ -160,7 +160,7 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques | |||
| 	} | ||||
| 
 | ||||
| 	// Discard any invalid upload before returning to avoid connection
 | ||||
| 	// reset error.
 | ||||
| 	// reset fm.Error.
 | ||||
| 	defer func() { | ||||
| 		io.Copy(ioutil.Discard, r.Body) | ||||
| 	}() | ||||
|  | @ -175,13 +175,13 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques | |||
| 
 | ||||
| 		// Otherwise we try to create the directory.
 | ||||
| 		err := c.User.FileSystem.Mkdir(r.URL.Path, 0776) | ||||
| 		return errorToHTTP(err, false), err | ||||
| 		return ErrorToHTTP(err, false), err | ||||
| 	} | ||||
| 
 | ||||
| 	// If using POST method, we are trying to create a new file so it is not
 | ||||
| 	// desirable to override an already existent file. Thus, we check
 | ||||
| 	// desirable to ovfm.Erride an already existent file. Thus, we check
 | ||||
| 	// if the file already exists. If so, we just return a 409 Conflict.
 | ||||
| 	if r.Method == http.MethodPost && r.Header.Get("Action") != "override" { | ||||
| 	if r.Method == http.MethodPost && r.Header.Get("Action") != "ovfm.Erride" { | ||||
| 		if _, err := c.User.FileSystem.Stat(r.URL.Path); err == nil { | ||||
| 			return http.StatusConflict, errors.New("There is already a file on that path") | ||||
| 		} | ||||
|  | @ -190,20 +190,20 @@ func resourcePostPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Reques | |||
| 	// Create/Open the file.
 | ||||
| 	f, err := c.User.FileSystem.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0776) | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, false), err | ||||
| 		return ErrorToHTTP(err, false), err | ||||
| 	} | ||||
| 	defer f.Close() | ||||
| 
 | ||||
| 	// Copies the new content for the file.
 | ||||
| 	_, err = io.Copy(f, r.Body) | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, false), err | ||||
| 		return ErrorToHTTP(err, false), err | ||||
| 	} | ||||
| 
 | ||||
| 	// Gets the info about the file.
 | ||||
| 	fi, err := f.Stat() | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, false), err | ||||
| 		return ErrorToHTTP(err, false), err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if this instance has a Static Generator and handles publishing
 | ||||
|  | @ -242,7 +242,7 @@ func resourcePublishSchedule(c *fm.Context, w http.ResponseWriter, r *http.Reque | |||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	c.cron.AddFunc(t.Format("05 04 15 02 01 *"), func() { | ||||
| 	c.Cron.AddFunc(t.Format("05 04 15 02 01 *"), func() { | ||||
| 		_, err := resourcePublish(c, w, r) | ||||
| 		if err != nil { | ||||
| 			log.Print(err) | ||||
|  | @ -283,7 +283,7 @@ func resourcePatchHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) | |||
| 	action := r.Header.Get("Action") | ||||
| 	dst, err := url.QueryUnescape(dst) | ||||
| 	if err != nil { | ||||
| 		return errorToHTTP(err, true), err | ||||
| 		return ErrorToHTTP(err, true), err | ||||
| 	} | ||||
| 
 | ||||
| 	src := r.URL.Path | ||||
|  | @ -298,7 +298,7 @@ func resourcePatchHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) | |||
| 		err = c.User.FileSystem.Rename(src, dst) | ||||
| 	} | ||||
| 
 | ||||
| 	return errorToHTTP(err, true), err | ||||
| 	return ErrorToHTTP(err, true), err | ||||
| } | ||||
| 
 | ||||
| // displayMode obtains the display mode from the Cookie.
 | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ type option struct { | |||
| func parsePutSettingsRequest(r *http.Request) (*modifySettingsRequest, error) { | ||||
| 	// Checks if the request body is empty.
 | ||||
| 	if r.Body == nil { | ||||
| 		return nil, errEmptyRequest | ||||
| 		return nil, fm.ErrEmptyRequest | ||||
| 	} | ||||
| 
 | ||||
| 	// Parses the request body and checks if it's well formed.
 | ||||
|  | @ -39,7 +39,7 @@ func parsePutSettingsRequest(r *http.Request) (*modifySettingsRequest, error) { | |||
| 
 | ||||
| 	// Checks if the request type is right.
 | ||||
| 	if mod.What != "settings" { | ||||
| 		return nil, errWrongDataType | ||||
| 		return nil, fm.ErrWrongDataType | ||||
| 	} | ||||
| 
 | ||||
| 	return mod, nil | ||||
|  | @ -103,9 +103,10 @@ func settingsPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) ( | |||
| 	if err != nil { | ||||
| 		return http.StatusBadRequest, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Update the commands.
 | ||||
| 	if mod.Which == "commands" { | ||||
| 		if err := c.db.Set("config", "commands", mod.Data.Commands); err != nil { | ||||
| 		if err := c.Store.Config.Save("commands", mod.Data.Commands); err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
| 
 | ||||
|  | @ -120,7 +121,7 @@ func settingsPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) ( | |||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
| 
 | ||||
| 		err = c.db.Set("staticgen", c.staticgen, c.StaticGen) | ||||
| 		err = c.Store.Config.Save("staticgen_"+c.StaticGen.Name(), c.StaticGen) | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
|  |  | |||
|  | @ -13,13 +13,6 @@ import ( | |||
| 	fm "github.com/hacdias/filemanager" | ||||
| ) | ||||
| 
 | ||||
| type shareLink struct { | ||||
| 	Hash       string    `json:"hash" storm:"id,index"` | ||||
| 	Path       string    `json:"path" storm:"index"` | ||||
| 	Expires    bool      `json:"expires"` | ||||
| 	ExpireDate time.Time `json:"expireDate"` | ||||
| } | ||||
| 
 | ||||
| func shareHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	r.URL.Path = sanitizeURL(r.URL.Path) | ||||
| 
 | ||||
|  | @ -36,12 +29,8 @@ 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) { | ||||
| 	var ( | ||||
| 		s    []*shareLink | ||||
| 		path = filepath.Join(string(c.User.FileSystem), r.URL.Path) | ||||
| 	) | ||||
| 
 | ||||
| 	err := c.db.Find("Path", path, &s) | ||||
| 	path := filepath.Join(string(c.User.FileSystem), r.URL.Path) | ||||
| 	s, err := c.Store.Share.GetByPath(path) | ||||
| 	if err == storm.ErrNotFound { | ||||
| 		return http.StatusNotFound, nil | ||||
| 	} | ||||
|  | @ -52,7 +41,7 @@ func shareGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 
 | ||||
| 	for i, link := range s { | ||||
| 		if link.Expires && link.ExpireDate.Before(time.Now()) { | ||||
| 			c.db.DeleteStruct(&shareLink{Hash: link.Hash}) | ||||
| 			c.Store.Share.Delete(link.Hash) | ||||
| 			s = append(s[:i], s[i+1:]...) | ||||
| 		} | ||||
| 	} | ||||
|  | @ -63,7 +52,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) | ||||
| 
 | ||||
| 	var s shareLink | ||||
| 	var s fm.ShareLink | ||||
| 	expire := r.URL.Query().Get("expires") | ||||
| 	unit := r.URL.Query().Get("unit") | ||||
| 
 | ||||
|  | @ -75,14 +64,14 @@ func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	bytes, err := generateRandomBytes(32) | ||||
| 	bytes, err := fm.GenerateRandomBytes(32) | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	str := hex.EncodeToString(bytes) | ||||
| 
 | ||||
| 	s = shareLink{ | ||||
| 	s = fm.ShareLink{ | ||||
| 		Path:    path, | ||||
| 		Hash:    str, | ||||
| 		Expires: expire != "", | ||||
|  | @ -109,8 +98,7 @@ func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| 		s.ExpireDate = time.Now().Add(add) | ||||
| 	} | ||||
| 
 | ||||
| 	err = c.db.Save(&s) | ||||
| 	if err != nil { | ||||
| 	if err := c.Store.Share.Save(&s); err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
|  | @ -118,9 +106,7 @@ func sharePostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| } | ||||
| 
 | ||||
| func shareDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	var s shareLink | ||||
| 
 | ||||
| 	err := c.db.One("Hash", strings.TrimPrefix(r.URL.Path, "/"), &s) | ||||
| 	s, err := c.Store.Share.Get(strings.TrimPrefix(r.URL.Path, "/")) | ||||
| 	if err == storm.ErrNotFound { | ||||
| 		return http.StatusNotFound, nil | ||||
| 	} | ||||
|  | @ -129,7 +115,7 @@ func shareDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) ( | |||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	err = c.db.DeleteStruct(&s) | ||||
| 	err = c.Store.Share.Delete(s.Hash) | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										122
									
								
								http/users.go
								
								
								
								
							
							
						
						
									
										122
									
								
								http/users.go
								
								
								
								
							|  | @ -9,7 +9,6 @@ import ( | |||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/asdine/storm" | ||||
| 	fm "github.com/hacdias/filemanager" | ||||
| ) | ||||
| 
 | ||||
|  | @ -48,7 +47,7 @@ func usersHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, e | |||
| 
 | ||||
| // getUserID returns the id from the user which is present
 | ||||
| // in the request url. If the url is invalid and doesn't
 | ||||
| // contain a valid ID, it returns an error.
 | ||||
| // contain a valid ID, it returns an fm.Error.
 | ||||
| func getUserID(r *http.Request) (int, error) { | ||||
| 	// Obtains the ID in string from the URL and converts
 | ||||
| 	// it into an integer.
 | ||||
|  | @ -64,11 +63,11 @@ 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 error.
 | ||||
| // returns an fm.Error.
 | ||||
| func getUser(r *http.Request) (*fm.User, string, error) { | ||||
| 	// Checks if the request body is empty.
 | ||||
| 	if r.Body == nil { | ||||
| 		return nil, "", errEmptyRequest | ||||
| 		return nil, "", fm.ErrEmptyRequest | ||||
| 	} | ||||
| 
 | ||||
| 	// Parses the request body and checks if it's well formed.
 | ||||
|  | @ -80,7 +79,7 @@ func getUser(r *http.Request) (*fm.User, string, error) { | |||
| 
 | ||||
| 	// Checks if the request type is right.
 | ||||
| 	if mod.What != "user" { | ||||
| 		return nil, "", errWrongDataType | ||||
| 		return nil, "", fm.ErrWrongDataType | ||||
| 	} | ||||
| 
 | ||||
| 	return mod.Data, mod.Which, nil | ||||
|  | @ -94,15 +93,15 @@ func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 
 | ||||
| 	// Request for the listing of users.
 | ||||
| 	if r.URL.Path == "/" { | ||||
| 		users := []User{} | ||||
| 		users, err := c.Store.Users.Gets() | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
| 
 | ||||
| 		for _, user := range c.Users { | ||||
| 			// Copies the user info and removes its
 | ||||
| 			// password so it won't be sent to the
 | ||||
| 			// front-end.
 | ||||
| 			u := *user | ||||
| 		for _, u := range users { | ||||
| 			// Removes the user password so it won't
 | ||||
| 			// be sent to the front-end.
 | ||||
| 			u.Password = "" | ||||
| 			users = append(users, u) | ||||
| 		} | ||||
| 
 | ||||
| 		sort.Slice(users, func(i, j int) bool { | ||||
|  | @ -117,19 +116,17 @@ func usersGetHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Searches for the user and prints the one who matches.
 | ||||
| 	for _, user := range c.Users { | ||||
| 		if user.ID != id { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		u := *user | ||||
| 		u.Password = "" | ||||
| 		return renderJSON(w, u) | ||||
| 	u, err := c.Store.Users.Get(id) | ||||
| 	if err == fm.ErrExist { | ||||
| 		return http.StatusNotFound, err | ||||
| 	} | ||||
| 
 | ||||
| 	// If there aren't any matches, return not found.
 | ||||
| 	return http.StatusNotFound, errUserNotExist | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	u.Password = "" | ||||
| 	return renderJSON(w, u) | ||||
| } | ||||
| 
 | ||||
| func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
|  | @ -144,17 +141,17 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| 
 | ||||
| 	// Checks if username isn't empty.
 | ||||
| 	if u.Username == "" { | ||||
| 		return http.StatusBadRequest, errEmptyUsername | ||||
| 		return http.StatusBadRequest, fm.ErrEmptyUsername | ||||
| 	} | ||||
| 
 | ||||
| 	// Checks if filesystem isn't empty.
 | ||||
| 	if u.FileSystem == "" { | ||||
| 		return http.StatusBadRequest, errEmptyScope | ||||
| 		return http.StatusBadRequest, fm.ErrEmptyScope | ||||
| 	} | ||||
| 
 | ||||
| 	// Checks if password isn't empty.
 | ||||
| 	if u.Password == "" { | ||||
| 		return http.StatusBadRequest, errEmptyPassword | ||||
| 		return http.StatusBadRequest, fm.ErrEmptyPassword | ||||
| 	} | ||||
| 
 | ||||
| 	// The username, password and scope cannot be empty.
 | ||||
|  | @ -164,7 +161,7 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| 
 | ||||
| 	// Initialize rules if they're not initialized.
 | ||||
| 	if u.Rules == nil { | ||||
| 		u.Rules = []*Rule{} | ||||
| 		u.Rules = []*fm.Rule{} | ||||
| 	} | ||||
| 
 | ||||
| 	// Initialize commands if not initialized.
 | ||||
|  | @ -183,7 +180,7 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| 	} | ||||
| 
 | ||||
| 	// Hashes the password.
 | ||||
| 	pw, err := hashPassword(u.Password) | ||||
| 	pw, err := fm.HashPassword(u.Password) | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
|  | @ -191,18 +188,15 @@ func usersPostHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (in | |||
| 	u.Password = pw | ||||
| 
 | ||||
| 	// Saves the user to the database.
 | ||||
| 	err = c.db.Save(u) | ||||
| 	if err == storm.ErrAlreadyExists { | ||||
| 		return http.StatusConflict, errUserExist | ||||
| 	err = c.Store.Users.Save(u) | ||||
| 	if err == fm.ErrExist { | ||||
| 		return http.StatusConflict, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Saves the user to the memory.
 | ||||
| 	c.Users[u.Username] = u | ||||
| 
 | ||||
| 	// Set the Location header and return.
 | ||||
| 	w.Header().Set("Location", "/users/"+strconv.Itoa(u.ID)) | ||||
| 	w.WriteHeader(http.StatusCreated) | ||||
|  | @ -243,23 +237,15 @@ func usersDeleteHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) ( | |||
| 	} | ||||
| 
 | ||||
| 	// Deletes the user from the database.
 | ||||
| 	err = c.db.DeleteStruct(&User{ID: id}) | ||||
| 	if err == storm.ErrNotFound { | ||||
| 		return http.StatusNotFound, errUserNotExist | ||||
| 	err = c.Store.Users.Delete(id) | ||||
| 	if err == fm.ErrNotExist { | ||||
| 		return http.StatusNotFound, fm.ErrNotExist | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Delete the user from the in-memory users map.
 | ||||
| 	for _, user := range c.Users { | ||||
| 		if user.ID == id { | ||||
| 			delete(c.Users, user.Username) | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return http.StatusOK, nil | ||||
| } | ||||
| 
 | ||||
|  | @ -290,12 +276,8 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 	if which == "partial" { | ||||
| 		c.User.CSS = u.CSS | ||||
| 		c.User.Locale = u.Locale | ||||
| 		err = c.db.UpdateField(&User{ID: c.User.ID}, "CSS", u.CSS) | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
| 
 | ||||
| 		err = c.db.UpdateField(&User{ID: c.User.ID}, "Locale", u.Locale) | ||||
| 		err = c.Store.Users.Update(c.User, "CSS", "Locale") | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
|  | @ -306,16 +288,15 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 	// Updates the Password.
 | ||||
| 	if which == "password" { | ||||
| 		if u.Password == "" { | ||||
| 			return http.StatusBadRequest, errEmptyPassword | ||||
| 			return http.StatusBadRequest, fm.ErrEmptyPassword | ||||
| 		} | ||||
| 
 | ||||
| 		pw, err := hashPassword(u.Password) | ||||
| 		c.User.Password, err = fm.HashPassword(u.Password) | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
| 
 | ||||
| 		c.User.Password = pw | ||||
| 		err = c.db.UpdateField(&User{ID: c.User.ID}, "Password", pw) | ||||
| 		err = c.Store.Users.Update(c.User, "Password") | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
|  | @ -325,17 +306,17 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 
 | ||||
| 	// If can only be all.
 | ||||
| 	if which != "all" { | ||||
| 		return http.StatusBadRequest, errInvalidUpdateField | ||||
| 		return http.StatusBadRequest, fm.ErrInvalidUpdateField | ||||
| 	} | ||||
| 
 | ||||
| 	// Checks if username isn't empty.
 | ||||
| 	if u.Username == "" { | ||||
| 		return http.StatusBadRequest, errEmptyUsername | ||||
| 		return http.StatusBadRequest, fm.ErrEmptyUsername | ||||
| 	} | ||||
| 
 | ||||
| 	// Checks if filesystem isn't empty.
 | ||||
| 	if u.FileSystem == "" { | ||||
| 		return http.StatusBadRequest, errEmptyScope | ||||
| 		return http.StatusBadRequest, fm.ErrEmptyScope | ||||
| 	} | ||||
| 
 | ||||
| 	// Checks if the scope exists.
 | ||||
|  | @ -345,7 +326,7 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 
 | ||||
| 	// Initialize rules if they're not initialized.
 | ||||
| 	if u.Rules == nil { | ||||
| 		u.Rules = []*Rule{} | ||||
| 		u.Rules = []*fm.Rule{} | ||||
| 	} | ||||
| 
 | ||||
| 	// Initialize commands if not initialized.
 | ||||
|  | @ -354,22 +335,20 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 	} | ||||
| 
 | ||||
| 	// Gets the current saved user from the in-memory map.
 | ||||
| 	var suser *User | ||||
| 	for _, user := range c.Users { | ||||
| 		if user.ID == id { | ||||
| 			suser = user | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if suser == nil { | ||||
| 	suser, err := c.Store.Users.Get(id) | ||||
| 	if err == fm.ErrNotExist { | ||||
| 		return http.StatusNotFound, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	u.ID = id | ||||
| 
 | ||||
| 	// Changes the password if the request wants it.
 | ||||
| 	if u.Password != "" { | ||||
| 		pw, err := hashPassword(u.Password) | ||||
| 		pw, err := fm.HashPassword(u.Password) | ||||
| 		if err != nil { | ||||
| 			return http.StatusInternalServerError, err | ||||
| 		} | ||||
|  | @ -381,17 +360,10 @@ func usersPutHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int | |||
| 
 | ||||
| 	// Updates the whole User struct because we always are supposed
 | ||||
| 	// to send a new entire object.
 | ||||
| 	err = c.db.Save(u) | ||||
| 	err = c.Store.Users.Update(u) | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
| 	} | ||||
| 
 | ||||
| 	// If the user changed the username, delete the old user
 | ||||
| 	// from the in-memory user map.
 | ||||
| 	if suser.Username != u.Username { | ||||
| 		delete(c.Users, suser.Username) | ||||
| 	} | ||||
| 
 | ||||
| 	c.Users[u.Username] = u | ||||
| 	return http.StatusOK, nil | ||||
| } | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ var ( | |||
| 
 | ||||
| // command handles the requests for VCS related commands: git, svn and mercurial
 | ||||
| func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	// Upgrades the connection to a websocket and checks for errors.
 | ||||
| 	// Upgrades the connection to a websocket and checks for fm.Errors.
 | ||||
| 	conn, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
|  | @ -92,7 +92,7 @@ func command(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) | |||
| 	cmd.Stderr = buff | ||||
| 	cmd.Stdout = buff | ||||
| 
 | ||||
| 	// Starts the command and checks for errors.
 | ||||
| 	// Starts the command and checks for fm.Errors.
 | ||||
| 	err = cmd.Start() | ||||
| 	if err != nil { | ||||
| 		return http.StatusInternalServerError, err | ||||
|  | @ -241,7 +241,7 @@ func parseSearch(value string) *searchOptions { | |||
| 
 | ||||
| // search searches for a file or directory.
 | ||||
| func search(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) { | ||||
| 	// Upgrades the connection to a websocket and checks for errors.
 | ||||
| 	// Upgrades the connection to a websocket and checks for fm.Errors.
 | ||||
| 	conn, err := upgrader.Upgrade(w, r, nil) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
|  |  | |||
							
								
								
									
										66
									
								
								user.go
								
								
								
								
							
							
						
						
									
										66
									
								
								user.go
								
								
								
								
							|  | @ -1,12 +1,28 @@ | |||
| package filemanager | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"errors" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| 
 | ||||
| 	"github.com/hacdias/fileutils" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	ErrExist              = errors.New("the resource already exists") | ||||
| 	ErrNotExist           = errors.New("the resource does not exist") | ||||
| 	ErrEmptyRequest       = errors.New("request body is empty") | ||||
| 	ErrEmptyPassword      = errors.New("password is empty") | ||||
| 	ErrEmptyUsername      = errors.New("username is empty") | ||||
| 	ErrEmptyScope         = errors.New("scope is empty") | ||||
| 	ErrWrongDataType      = errors.New("wrong data type") | ||||
| 	ErrInvalidUpdateField = errors.New("invalid field to update") | ||||
| ) | ||||
| 
 | ||||
| // DefaultUser is used on New, when no 'base' user is provided.
 | ||||
| var DefaultUser = User{ | ||||
| 	AllowCommands: true, | ||||
|  | @ -110,10 +126,24 @@ func (r *Regexp) MatchString(s string) bool { | |||
| 	return r.regexp.MatchString(s) | ||||
| } | ||||
| 
 | ||||
| type ShareLink struct { | ||||
| 	Hash       string    `json:"hash" storm:"id,index"` | ||||
| 	Path       string    `json:"path" storm:"index"` | ||||
| 	Expires    bool      `json:"expires"` | ||||
| 	ExpireDate time.Time `json:"expireDate"` | ||||
| } | ||||
| 
 | ||||
| type Store struct { | ||||
| 	Users  UsersStore | ||||
| 	Config ConfigStore | ||||
| 	Share  ShareStore | ||||
| } | ||||
| 
 | ||||
| type UsersStore interface { | ||||
| 	Get(id int) (*User, error) | ||||
| 	Gets() ([]*User, error) | ||||
| 	Save(u *User, fields ...string) error | ||||
| 	Save(u *User) error | ||||
| 	Update(u *User, fields ...string) error | ||||
| 	Delete(id int) error | ||||
| } | ||||
| 
 | ||||
|  | @ -123,6 +153,36 @@ type ConfigStore interface { | |||
| } | ||||
| 
 | ||||
| type ShareStore interface { | ||||
| 	Get(hash string) | ||||
| 	Save() | ||||
| 	Get(hash string) (*ShareLink, error) | ||||
| 	GetByPath(path string) ([]*ShareLink, error) | ||||
| 	Gets() ([]*ShareLink, error) | ||||
| 	Save(s *ShareLink) error | ||||
| 	Delete(hash string) error | ||||
| } | ||||
| 
 | ||||
| // HashPassword generates an hash from a password using bcrypt.
 | ||||
| func HashPassword(password string) (string, error) { | ||||
| 	bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) | ||||
| 	return string(bytes), err | ||||
| } | ||||
| 
 | ||||
| // CheckPasswordHash compares a password with an hash to check if they match.
 | ||||
| func CheckPasswordHash(password, hash string) bool { | ||||
| 	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) | ||||
| 	return err == nil | ||||
| } | ||||
| 
 | ||||
| // GenerateRandomBytes returns securely generated random bytes.
 | ||||
| // It will return an fm.Error if the system's secure random
 | ||||
| // number generator fails to function correctly, in which
 | ||||
| // case the caller should not continue.
 | ||||
| func GenerateRandomBytes(n int) ([]byte, error) { | ||||
| 	b := make([]byte, n) | ||||
| 	_, err := rand.Read(b) | ||||
| 	// Note that err == nil only if we read len(b) bytes.
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return b, nil | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue