more updates
License: MIT Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
parent
18ad9d78b3
commit
9f58170575
@ -1,185 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
/*
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"mime"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/files"
|
|
||||||
"github.com/filebrowser/filebrowser/rules"
|
|
||||||
"github.com/filebrowser/filebrowser/settings"
|
|
||||||
"github.com/filebrowser/filebrowser/users"
|
|
||||||
"github.com/mholt/caddy"
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// FileBrowser represents a File Browser instance which must
|
|
||||||
// be created through NewFileBrowser.
|
|
||||||
type FileBrowser struct {
|
|
||||||
settings *settings.Settings
|
|
||||||
mux sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFileBrowser creates a new File Browser instance from a
|
|
||||||
// storage backend. If that backend doesn't contain settings
|
|
||||||
// on it (returns ErrNotExist), then we generate a new key
|
|
||||||
// and base settings.
|
|
||||||
func NewFileBrowser(backend StorageBackend) (*FileBrowser, error) {
|
|
||||||
set, err := backend.GetSettings()
|
|
||||||
|
|
||||||
if err == ErrNotExist {
|
|
||||||
var key []byte
|
|
||||||
key, err = generateRandomBytes(64)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
set = &settings.Settings{Key: key}
|
|
||||||
err = backend.SaveSettings(set)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &FileBrowser{
|
|
||||||
settings: set,
|
|
||||||
storage: backend,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RLockSettings locks the settings for reading.
|
|
||||||
func (f *FileBrowser) RLockSettings() {
|
|
||||||
f.mux.RLock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RUnlockSettings unlocks the settings for reading.
|
|
||||||
func (f *FileBrowser) RUnlockSettings() {
|
|
||||||
f.mux.RUnlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckRules matches the rules against user rules and global rules.
|
|
||||||
func (f *FileBrowser) CheckRules(path string, user *users.User) bool {
|
|
||||||
f.RLockSettings()
|
|
||||||
val := rules.Check(path, user, f.settings)
|
|
||||||
f.RUnlockSettings()
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunHook runs the hooks for the before and after event.
|
|
||||||
func (f *FileBrowser) RunHook(fn func() error, evt, path, dst string, user *users.User) error {
|
|
||||||
path = user.FullPath(path)
|
|
||||||
dst = user.FullPath(dst)
|
|
||||||
|
|
||||||
if val, ok := f.settings.Commands["before_"+evt]; ok {
|
|
||||||
for _, command := range val {
|
|
||||||
err := f.exec(command, "before_"+evt, path, dst, user)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := fn()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if val, ok := f.settings.Commands["after_"+evt]; ok {
|
|
||||||
for _, command := range val {
|
|
||||||
err := f.exec(command, "after_"+evt, path, dst, user)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseCommand parses the command taking in account if the current
|
|
||||||
// instance uses a shell to run the commands or just calls the binary
|
|
||||||
// directyly.
|
|
||||||
func (f *FileBrowser) ParseCommand(raw string) ([]string, error) {
|
|
||||||
f.RLockSettings()
|
|
||||||
defer f.RUnlockSettings()
|
|
||||||
|
|
||||||
command := []string{}
|
|
||||||
|
|
||||||
if len(f.settings.Shell) == 0 {
|
|
||||||
cmd, args, err := caddy.SplitCommandAndArgs(raw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = exec.LookPath(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
command = append(command, cmd)
|
|
||||||
command = append(command, args...)
|
|
||||||
} else {
|
|
||||||
command = append(f.settings.Shell, raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
return command, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func (f *FileBrowser) exec(raw, evt, path, dst string, user *users.User) error {
|
|
||||||
blocking := true
|
|
||||||
|
|
||||||
if strings.HasSuffix(raw, "&") {
|
|
||||||
blocking = false
|
|
||||||
raw = strings.TrimSpace(strings.TrimSuffix(raw, "&"))
|
|
||||||
}
|
|
||||||
|
|
||||||
command, err := f.ParseCommand(raw)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command(command[0], command[1:]...)
|
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path))
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope))
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", evt))
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("USERNAME=%s", user.Username))
|
|
||||||
cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst))
|
|
||||||
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
if !blocking {
|
|
||||||
log.Printf("[INFO] Nonblocking Command: \"%s\"", strings.Join(command, " "))
|
|
||||||
return cmd.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[INFO] Blocking Command: \"%s\"", strings.Join(command, " "))
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@ -8,6 +8,7 @@ var (
|
|||||||
ErrNotExist = errors.New("the resource does not exist")
|
ErrNotExist = errors.New("the resource does not exist")
|
||||||
ErrEmptyPassword = errors.New("password is empty")
|
ErrEmptyPassword = errors.New("password is empty")
|
||||||
ErrEmptyUsername = errors.New("username is empty")
|
ErrEmptyUsername = errors.New("username is empty")
|
||||||
|
ErrEmptyRequest = errors.New("empty request")
|
||||||
ErrScopeIsRelative = errors.New("scope is a relative path")
|
ErrScopeIsRelative = errors.New("scope is a relative path")
|
||||||
ErrInvalidDataType = errors.New("invalid data type")
|
ErrInvalidDataType = errors.New("invalid data type")
|
||||||
ErrIsDirectory = errors.New("file is directory")
|
ErrIsDirectory = errors.New("file is directory")
|
||||||
|
|||||||
193
http/auth.go
193
http/auth.go
@ -1,7 +1,6 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -14,76 +13,6 @@ import (
|
|||||||
"github.com/filebrowser/filebrowser/users"
|
"github.com/filebrowser/filebrowser/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *env) loginHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, err := e.auther.Auth(r)
|
|
||||||
if err == os.ErrPermission {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
} else if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
} else {
|
|
||||||
e.printToken(w, r, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type signupBody struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) signupHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
settings, err := e.Settings.Get()
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !settings.Signup {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Body == nil {
|
|
||||||
httpErr(w, r, http.StatusBadRequest, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
info := &signupBody{}
|
|
||||||
err = json.NewDecoder(r.Body).Decode(info)
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusBadRequest, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Password == "" || info.Username == "" {
|
|
||||||
httpErr(w, r, http.StatusBadRequest, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user := &users.User{
|
|
||||||
Username: info.Username,
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.Defaults.Apply(user)
|
|
||||||
|
|
||||||
pwd, err := users.HashPwd(info.Password)
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Password = pwd
|
|
||||||
err = e.Users.Save(user)
|
|
||||||
if err == errors.ErrExist {
|
|
||||||
httpErr(w, r, http.StatusConflict, nil)
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpErr(w, r, http.StatusOK, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
type userInfo struct {
|
type userInfo struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
@ -118,28 +47,17 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
|
|||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) auth(next http.HandlerFunc) http.HandlerFunc {
|
func withUser(fn handleFunc) handleFunc {
|
||||||
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
settings, err := e.Settings.Get()
|
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
||||||
if err != nil {
|
return d.settings.Key, nil
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.Key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
nextWithUser := func(w http.ResponseWriter, r *http.Request, id uint) {
|
|
||||||
ctx := context.WithValue(r.Context(), keyUserID, id)
|
|
||||||
next(w, r.WithContext(ctx))
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var tk authToken
|
var tk authToken
|
||||||
token, err := request.ParseFromRequestWithClaims(r, &extractor{}, &tk, keyFunc)
|
token, err := request.ParseFromRequestWithClaims(r, &extractor{}, &tk, keyFunc)
|
||||||
|
|
||||||
if err != nil || !token.Valid {
|
if err != nil || !token.Valid {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tk.VerifyExpiresAt(time.Now().Add(time.Hour).Unix(), true) {
|
if !tk.VerifyExpiresAt(time.Now().Add(time.Hour).Unix(), true) {
|
||||||
@ -147,20 +65,88 @@ func (e *env) auth(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
w.Header().Add("X-Renew-Token", "true")
|
w.Header().Add("X-Renew-Token", "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
nextWithUser(w, r, tk.User.ID)
|
d.user, err = d.store.Users.Get(tk.User.ID)
|
||||||
|
return fn(w, r, d)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) renew(w http.ResponseWriter, r *http.Request) {
|
func withAdmin(fn handleFunc) handleFunc {
|
||||||
user, ok := e.getUser(w, r)
|
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
if !ok {
|
if !d.user.Perm.Admin {
|
||||||
return
|
return http.StatusForbidden, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
e.printToken(w, r, user)
|
return fn(w, r, d)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) printToken(w http.ResponseWriter, r *http.Request, user *users.User) {
|
var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
auther, err := d.store.Auth.Get(d.settings.AuthMethod)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := auther.Auth(r)
|
||||||
|
if err == os.ErrPermission {
|
||||||
|
return http.StatusForbidden, nil
|
||||||
|
} else if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
} else {
|
||||||
|
return printToken(w, r, d, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type signupBody struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
if !d.settings.Signup {
|
||||||
|
return http.StatusMethodNotAllowed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Body == nil {
|
||||||
|
return http.StatusBadRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
info := &signupBody{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(info)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusBadRequest, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Password == "" || info.Username == "" {
|
||||||
|
return http.StatusBadRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user := &users.User{
|
||||||
|
Username: info.Username,
|
||||||
|
}
|
||||||
|
|
||||||
|
d.settings.Defaults.Apply(user)
|
||||||
|
|
||||||
|
pwd, err := users.HashPwd(info.Password)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Password = pwd
|
||||||
|
err = d.store.Users.Save(user)
|
||||||
|
if err == errors.ErrExist {
|
||||||
|
return http.StatusConflict, err
|
||||||
|
} else if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
return printToken(w, r, d, d.user)
|
||||||
|
})
|
||||||
|
|
||||||
|
func printToken(w http.ResponseWriter, r *http.Request, d *data, user *users.User) (int, error) {
|
||||||
claims := &authToken{
|
claims := &authToken{
|
||||||
User: userInfo{
|
User: userInfo{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
@ -177,19 +163,12 @@ func (e *env) printToken(w http.ResponseWriter, r *http.Request, user *users.Use
|
|||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
|
signed, err := token.SignedString(d.settings.Key)
|
||||||
settings, err := e.Settings.Get()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
signed, err := token.SignedString(settings.Key)
|
w.Header().Set("Content-Type", "cty")
|
||||||
|
w.Write([]byte(signed))
|
||||||
if err != nil {
|
return 0, nil
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Type", "cty")
|
|
||||||
w.Write([]byte(signed))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
70
http/data.go
Normal file
70
http/data.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/runner"
|
||||||
|
"github.com/filebrowser/filebrowser/settings"
|
||||||
|
"github.com/filebrowser/filebrowser/storage"
|
||||||
|
"github.com/filebrowser/filebrowser/users"
|
||||||
|
)
|
||||||
|
|
||||||
|
type handleFunc func(w http.ResponseWriter, r *http.Request, d *data) (int, error)
|
||||||
|
|
||||||
|
type data struct {
|
||||||
|
*runner.Runner
|
||||||
|
settings *settings.Settings
|
||||||
|
store *storage.Storage
|
||||||
|
user *users.User
|
||||||
|
raw interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check implements rules.Checker.
|
||||||
|
func (d *data) Check(path string) bool {
|
||||||
|
for _, rule := range d.user.Rules {
|
||||||
|
if rule.Matches(path) {
|
||||||
|
return rule.Allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range d.settings.Rules {
|
||||||
|
if rule.Matches(path) {
|
||||||
|
return rule.Allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle(fn handleFunc, prefix string, storage *storage.Storage) http.Handler {
|
||||||
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
settings, err := storage.Settings.Get()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("ERROR: couldn't get settings")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := fn(w, r, &data{
|
||||||
|
Runner: &runner.Runner{Settings: settings},
|
||||||
|
store: storage,
|
||||||
|
settings: settings,
|
||||||
|
})
|
||||||
|
|
||||||
|
if status != 0 {
|
||||||
|
txt := http.StatusText(status)
|
||||||
|
http.Error(w, strconv.Itoa(status)+" "+txt, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status >= 400 || err != nil {
|
||||||
|
log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if prefix == "" {
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StripPrefix(prefix, handler)
|
||||||
|
}
|
||||||
125
http/http.go
125
http/http.go
@ -1,24 +1,10 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/auth"
|
|
||||||
"github.com/filebrowser/filebrowser/storage"
|
"github.com/filebrowser/filebrowser/storage"
|
||||||
"github.com/filebrowser/filebrowser/errors"
|
|
||||||
"github.com/filebrowser/filebrowser/users"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type key int
|
|
||||||
|
|
||||||
const (
|
|
||||||
keyUserID key = iota
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type modifyRequest struct {
|
type modifyRequest struct {
|
||||||
@ -26,67 +12,54 @@ type modifyRequest struct {
|
|||||||
Which []string `json:"which"` // Answer to: which fields?
|
Which []string `json:"which"` // Answer to: which fields?
|
||||||
}
|
}
|
||||||
|
|
||||||
type env struct {
|
|
||||||
*storage.Storage
|
|
||||||
auther auth.Auther
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewHandler builds an HTTP handler on the top of a File Browser instance.
|
|
||||||
func NewHandler(storage *storage.Storage) (http.Handler, error) {
|
func NewHandler(storage *storage.Storage) (http.Handler, error) {
|
||||||
/* authMethod := fb.GetSettings().AuthMethod
|
|
||||||
auther, err := fb.GetAuther(authMethod)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} */
|
|
||||||
|
|
||||||
e := &env{}
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
index, static := e.getStaticHandlers()
|
/* index, static := e.getStaticHandlers()
|
||||||
|
|
||||||
r.PathPrefix("/static").Handler(static)
|
r.PathPrefix("/static").Handler(static)
|
||||||
r.NotFoundHandler = index
|
r.NotFoundHandler = index */
|
||||||
|
|
||||||
api := r.PathPrefix("/api").Subrouter()
|
api := r.PathPrefix("/api").Subrouter()
|
||||||
api.HandleFunc("/login", e.loginHandler)
|
|
||||||
api.HandleFunc("/signup", e.signupHandler)
|
|
||||||
api.HandleFunc("/renew", e.auth(e.renew))
|
|
||||||
|
|
||||||
users := api.PathPrefix("/users").Subrouter()
|
api.Handle("/login", handle(loginHandler, "", storage))
|
||||||
|
api.Handle("/signup", handle(signupHandler, "", storage))
|
||||||
|
api.Handle("/renew", handle(renewHandler, "", storage))
|
||||||
|
|
||||||
|
/* users := api.PathPrefix("/users").Subrouter()
|
||||||
users.HandleFunc("", e.auth(e.usersGetHandler)).Methods("GET")
|
users.HandleFunc("", e.auth(e.usersGetHandler)).Methods("GET")
|
||||||
users.HandleFunc("", e.auth(e.userPostHandler)).Methods("POST")
|
users.HandleFunc("", e.auth(e.userPostHandler)).Methods("POST")
|
||||||
users.HandleFunc("/{id:[0-9]+}", e.auth(e.userPutHandler)).Methods("PUT")
|
users.HandleFunc("/{id:[0-9]+}", e.auth(e.userPutHandler)).Methods("PUT")
|
||||||
users.HandleFunc("/{id:[0-9]+}", e.auth(e.userGetHandler)).Methods("GET")
|
users.HandleFunc("/{id:[0-9]+}", e.auth(e.userGetHandler)).Methods("GET")
|
||||||
users.HandleFunc("/{id:[0-9]+}", e.auth(e.userDeleteHandler)).Methods("DELETE")
|
users.HandleFunc("/{id:[0-9]+}", e.auth(e.userDeleteHandler)).Methods("DELETE") */
|
||||||
|
|
||||||
api.PathPrefix("/resources").HandlerFunc(e.auth(e.resourceGetHandler)).Methods("GET")
|
api.PathPrefix("/resources").Handler(handle(resourceGetHandler, "/api/resources", storage)).Methods("GET")
|
||||||
api.PathPrefix("/resources").HandlerFunc(e.auth(e.resourceDeleteHandler)).Methods("DELETE")
|
api.PathPrefix("/resources").Handler(handle(resourceDeleteHandler, "/api/resources", storage)).Methods("DELETE")
|
||||||
api.PathPrefix("/resources").HandlerFunc(e.auth(e.resourcePostPutHandler)).Methods("POST")
|
api.PathPrefix("/resources").Handler(handle(resourcePostPutHandler, "/api/resources", storage)).Methods("POST")
|
||||||
api.PathPrefix("/resources").HandlerFunc(e.auth(e.resourcePostPutHandler)).Methods("PUT")
|
api.PathPrefix("/resources").Handler(handle(resourcePostPutHandler, "/api/resources", storage)).Methods("PUT")
|
||||||
api.PathPrefix("/resources").HandlerFunc(e.auth(e.resourcePatchHandler)).Methods("PATCH")
|
api.PathPrefix("/resources").Handler(handle(resourcePatchHandler, "/api/resources", storage)).Methods("PATCH")
|
||||||
|
|
||||||
api.PathPrefix("/share").HandlerFunc(e.auth(e.shareGetHandler)).Methods("GET")
|
api.PathPrefix("/share").Handler(handle(shareGetHandler, "/api/share", storage)).Methods("GET")
|
||||||
api.PathPrefix("/share").HandlerFunc(e.auth(e.sharePostHandler)).Methods("POST")
|
api.PathPrefix("/share").Handler(handle(sharePostHandler, "/api/share", storage)).Methods("POST")
|
||||||
api.PathPrefix("/share").HandlerFunc(e.auth(e.shareDeleteHandler)).Methods("DELETE")
|
api.PathPrefix("/share").Handler(handle(shareDeleteHandler, "/api/share", storage)).Methods("DELETE")
|
||||||
|
|
||||||
api.HandleFunc("/settings", e.auth(e.settingsGetHandler)).Methods("GET")
|
api.Handle("/settings", handle(settingsGetHandler, "", storage)).Methods("GET")
|
||||||
api.HandleFunc("/settings", e.auth(e.settingsPutHandler)).Methods("PUT")
|
api.Handle("/settings", handle(settingsPutHandler, "", storage)).Methods("PUT")
|
||||||
|
|
||||||
api.PathPrefix("/raw").HandlerFunc(e.auth(e.rawHandler)).Methods("GET")
|
/* api.PathPrefix("/raw").HandlerFunc(e.auth(e.rawHandler)).Methods("GET")
|
||||||
api.PathPrefix("/command").HandlerFunc(e.auth(e.commandsHandler))
|
api.PathPrefix("/command").HandlerFunc(e.auth(e.commandsHandler))
|
||||||
api.PathPrefix("/search").HandlerFunc(e.auth(e.searchHandler))
|
api.PathPrefix("/search").HandlerFunc(e.auth(e.searchHandler)) */
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpErr(w http.ResponseWriter, r *http.Request, status int, err error) {
|
|
||||||
txt := http.StatusText(status)
|
/*
|
||||||
if err != nil || status >= 400 {
|
type key int
|
||||||
log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err)
|
|
||||||
}
|
|
||||||
http.Error(w, strconv.Itoa(status)+" "+txt, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) {
|
func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) {
|
||||||
txt := http.StatusText(status)
|
txt := http.StatusText(status)
|
||||||
@ -95,46 +68,4 @@ func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) {
|
|||||||
}
|
}
|
||||||
ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(10*time.Second))
|
ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(10*time.Second))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
func renderJSON(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|
||||||
marsh, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
|
||||||
if _, err := w.Write(marsh); err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) getUser(w http.ResponseWriter, r *http.Request) (*users.User, bool) {
|
|
||||||
id := r.Context().Value(keyUserID).(uint)
|
|
||||||
user, err := e.Users.Get(id)
|
|
||||||
if err == errors.ErrNotExist {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) getAdminUser(w http.ResponseWriter, r *http.Request) (*users.User, bool) {
|
|
||||||
user, ok := e.getUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.Perm.Admin {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, true
|
|
||||||
}
|
|
||||||
|
|||||||
20
http/raw.go
20
http/raw.go
@ -1,18 +1,6 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
/*
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/files"
|
|
||||||
"github.com/filebrowser/filebrowser/users"
|
|
||||||
"github.com/hacdias/fileutils"
|
|
||||||
"github.com/mholt/archiver"
|
|
||||||
)
|
|
||||||
|
|
||||||
const apiRawPrefix = "/api/raw"
|
const apiRawPrefix = "/api/raw"
|
||||||
|
|
||||||
func parseQueryFiles(r *http.Request, f *files.FileInfo, u *users.User) ([]string, error) {
|
func parseQueryFiles(r *http.Request, f *files.FileInfo, u *users.User) ([]string, error) {
|
||||||
@ -68,9 +56,7 @@ func (e *env) rawHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
files.NewFileInfo(user.Fs, path, user.Perm.Modify)
|
file, err := files.NewFileInfo(user.Fs, path, user.Perm.Modify)
|
||||||
|
|
||||||
file, err := e.NewFile(path, user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
httpErr(w, r, httpFsErr(err), err)
|
||||||
return
|
return
|
||||||
@ -160,4 +146,4 @@ func fileHandler(w http.ResponseWriter, r *http.Request, file *files.File, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
http.ServeContent(w, r, file.Name, file.ModTime, fd)
|
http.ServeContent(w, r, file.Name, file.ModTime, fd)
|
||||||
}
|
} */
|
||||||
|
|||||||
174
http/resource.go
174
http/resource.go
@ -9,116 +9,64 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/fileutils"
|
"github.com/filebrowser/filebrowser/files"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/users"
|
"github.com/filebrowser/filebrowser/errors"
|
||||||
|
"github.com/filebrowser/filebrowser/fileutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const apiResourcePrefix = "/api/resources"
|
// TODO: trim path
|
||||||
|
|
||||||
func httpFsErr(err error) int {
|
var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
switch {
|
file, err := files.NewFileInfo(d.user.Fs, r.URL.Path, d.user.Perm.Modify, d)
|
||||||
case err == nil:
|
|
||||||
return http.StatusOK
|
|
||||||
case os.IsPermission(err):
|
|
||||||
return http.StatusForbidden
|
|
||||||
case os.IsNotExist(err):
|
|
||||||
return http.StatusNotFound
|
|
||||||
case os.IsExist(err):
|
|
||||||
return http.StatusConflict
|
|
||||||
default:
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) getResourceData(w http.ResponseWriter, r *http.Request, prefix string) (string, *users.User, bool) {
|
|
||||||
user, ok := e.getUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return "", nil, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
path := strings.TrimPrefix(r.URL.Path, prefix)
|
|
||||||
path = strings.TrimSuffix(path, "/")
|
|
||||||
if path == "" {
|
|
||||||
path = "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
return path, user, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) resourceGetHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
path, user, ok := e.getResourceData(w, r, apiResourcePrefix)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := e.NewFile(path, user)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
return errToStatus(err), err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if file.IsDir {
|
if file.IsDir {
|
||||||
file.Listing.Sorting = user.Sorting
|
file.Listing.Sorting = d.user.Sorting
|
||||||
file.Listing.ApplySort()
|
file.Listing.ApplySort()
|
||||||
renderJSON(w, r, file)
|
return renderJSON(w, r, file)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if checksum := r.URL.Query().Get("checksum"); checksum != "" {
|
if checksum := r.URL.Query().Get("checksum"); checksum != "" {
|
||||||
err = e.Checksum(file, user, checksum)
|
err := file.Checksum(checksum)
|
||||||
if err == lib.ErrInvalidOption {
|
if err == errors.ErrInvalidOption {
|
||||||
httpErr(w, r, http.StatusBadRequest, nil)
|
return http.StatusBadRequest, nil
|
||||||
return
|
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not waste bandwidth if we just want the checksum
|
// do not waste bandwidth if we just want the checksum
|
||||||
file.Content = ""
|
file.Content = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJSON(w, r, file)
|
return renderJSON(w, r, file)
|
||||||
}
|
})
|
||||||
|
|
||||||
func (e *env) resourceDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
var resourceDeleteHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
path, user, ok := e.getResourceData(w, r, apiResourcePrefix)
|
if r.URL.Path == "/" || !d.user.Perm.Delete {
|
||||||
if !ok {
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if path == "/" || !user.Perm.Delete {
|
err := d.RunHook(func() error {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return d.user.Fs.RemoveAll(r.URL.Path)
|
||||||
return
|
}, "delete", r.URL.Path, "", d.user)
|
||||||
}
|
|
||||||
|
|
||||||
err := e.RunHook(func() error {
|
|
||||||
return user.Fs.RemoveAll(path)
|
|
||||||
}, "delete", path, "", user)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
return errToStatus(err), err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
return http.StatusOK, nil
|
||||||
}
|
})
|
||||||
|
|
||||||
func (e *env) resourcePostPutHandler(w http.ResponseWriter, r *http.Request) {
|
var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
path, user, ok := e.getResourceData(w, r, apiResourcePrefix)
|
if !d.user.Perm.Create && r.Method == http.MethodPost {
|
||||||
if !ok {
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.Perm.Create && r.Method == http.MethodPost {
|
if !d.user.Perm.Modify && r.Method == http.MethodPut {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.Perm.Modify && r.Method == http.MethodPut {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -128,24 +76,21 @@ func (e *env) resourcePostPutHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
// For directories, only allow POST for creation.
|
// For directories, only allow POST for creation.
|
||||||
if strings.HasSuffix(r.URL.Path, "/") {
|
if strings.HasSuffix(r.URL.Path, "/") {
|
||||||
if r.Method == http.MethodPut {
|
if r.Method == http.MethodPut {
|
||||||
httpErr(w, r, http.StatusMethodNotAllowed, nil)
|
return http.StatusMethodNotAllowed, nil
|
||||||
} else {
|
|
||||||
err := user.Fs.MkdirAll(path, 0775)
|
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
err := d.user.Fs.MkdirAll(r.URL.Path, 0775)
|
||||||
|
return errToStatus(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == http.MethodPost && r.URL.Query().Get("override") != "true" {
|
if r.Method == http.MethodPost && r.URL.Query().Get("override") != "true" {
|
||||||
if _, err := user.Fs.Stat(path); err == nil {
|
if _, err := d.user.Fs.Stat(r.URL.Path); err == nil {
|
||||||
httpErr(w, r, http.StatusConflict, nil)
|
return http.StatusConflict, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.RunHook(func() error {
|
err := d.RunHook(func() error {
|
||||||
file, err := user.Fs.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
file, err := d.user.Fs.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -165,58 +110,45 @@ func (e *env) resourcePostPutHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
|
etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
|
||||||
w.Header().Set("ETag", etag)
|
w.Header().Set("ETag", etag)
|
||||||
return nil
|
return nil
|
||||||
}, "upload", path, "", user)
|
}, "upload", r.URL.Path, "", d.user)
|
||||||
|
|
||||||
if err != nil {
|
return errToStatus(err), err
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
})
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
httpErr(w, r, http.StatusOK, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) resourcePatchHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
src, user, ok := e.getResourceData(w, r, apiResourcePrefix)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
src := r.URL.Path
|
||||||
dst := r.URL.Query().Get("destination")
|
dst := r.URL.Query().Get("destination")
|
||||||
action := r.URL.Query().Get("action")
|
action := r.URL.Query().Get("action")
|
||||||
dst, err := url.QueryUnescape(dst)
|
dst, err := url.QueryUnescape(dst)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
return errToStatus(err), err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if dst == "/" || src == "/" {
|
if dst == "/" || src == "/" {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case "copy":
|
case "copy":
|
||||||
if !user.Perm.Create {
|
if !d.user.Perm.Create {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
case "rename":
|
case "rename":
|
||||||
default:
|
default:
|
||||||
action = "rename"
|
action = "rename"
|
||||||
if !user.Perm.Rename {
|
if !d.user.Perm.Rename {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return http.StatusForbidden, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.RunHook(func() error {
|
err = d.RunHook(func() error {
|
||||||
if action == "copy" {
|
if action == "copy" {
|
||||||
return fileutils.Copy(user.Fs, src, dst)
|
return fileutils.Copy(d.user.Fs, src, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
return user.Fs.Rename(src, dst)
|
return d.user.Fs.Rename(src, dst)
|
||||||
}, action, src, dst, user)
|
}, action, src, dst, d.user)
|
||||||
|
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
return errToStatus(err), err
|
||||||
}
|
})
|
||||||
|
|||||||
@ -17,63 +17,33 @@ type settingsData struct {
|
|||||||
Commands map[string][]string `json:"commands"`
|
Commands map[string][]string `json:"commands"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) settingsGetHandler(w http.ResponseWriter, r *http.Request) {
|
var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
_, ok := e.getAdminUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
settings, err := e.Settings.Get()
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data := &settingsData{
|
data := &settingsData{
|
||||||
Signup: settings.Signup,
|
Signup: d.settings.Signup,
|
||||||
Defaults: settings.Defaults,
|
Defaults: d.settings.Defaults,
|
||||||
Rules: settings.Rules,
|
Rules: d.settings.Rules,
|
||||||
Branding: settings.Branding,
|
Branding: d.settings.Branding,
|
||||||
Shell: settings.Shell,
|
Shell: d.settings.Shell,
|
||||||
Commands: settings.Commands,
|
Commands: d.settings.Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJSON(w, r, data)
|
return renderJSON(w, r, data)
|
||||||
}
|
})
|
||||||
|
|
||||||
func (e *env) settingsPutHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
_, ok := e.getAdminUser(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
req := &settingsData{}
|
req := &settingsData{}
|
||||||
err := json.NewDecoder(r.Body).Decode(req)
|
err := json.NewDecoder(r.Body).Decode(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusBadRequest, err)
|
return http.StatusBadRequest, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings, err := e.Settings.Get()
|
d.settings.Signup = req.Signup
|
||||||
if err != nil {
|
d.settings.Defaults = req.Defaults
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
d.settings.Rules = req.Rules
|
||||||
return
|
d.settings.Branding = req.Branding
|
||||||
}
|
d.settings.Shell = req.Shell
|
||||||
|
d.settings.Commands = req.Commands
|
||||||
|
|
||||||
if err != nil {
|
err = d.store.Settings.Save(d.settings)
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return errToStatus(err), err
|
||||||
return
|
})
|
||||||
}
|
|
||||||
|
|
||||||
settings.Signup = req.Signup
|
|
||||||
settings.Defaults = req.Defaults
|
|
||||||
settings.Rules = req.Rules
|
|
||||||
settings.Branding = req.Branding
|
|
||||||
settings.Shell = req.Shell
|
|
||||||
settings.Commands = req.Commands
|
|
||||||
|
|
||||||
err = e.Settings.Save(settings)
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
102
http/share.go
102
http/share.go
@ -12,104 +12,72 @@ import (
|
|||||||
"github.com/filebrowser/filebrowser/share"
|
"github.com/filebrowser/filebrowser/share"
|
||||||
)
|
)
|
||||||
|
|
||||||
const apiSharePrefix = "/api/share"
|
func withPermShare(fn handleFunc) handleFunc {
|
||||||
|
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
if !d.user.Perm.Share {
|
||||||
|
return http.StatusForbidden, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *env) getShareData(w http.ResponseWriter, r *http.Request, prefix string) (string, bool) {
|
return fn(w, r, d)
|
||||||
relPath, user, ok := e.getResourceData(w, r, apiSharePrefix)
|
})
|
||||||
if !ok {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.Perm.Share {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
return user.FullPath(relPath), ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) shareGetHandler(w http.ResponseWriter, r *http.Request) {
|
var shareGetHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
path, ok := e.getShareData(w, r, apiSharePrefix)
|
s, err := d.store.Share.Gets(r.URL.Path)
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := e.Share.Gets(path)
|
|
||||||
if err == errors.ErrNotExist {
|
if err == errors.ErrNotExist {
|
||||||
renderJSON(w, r, []*share.Link{})
|
return renderJSON(w, r, []*share.Link{})
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.Share.Delete(link.Hash)
|
d.store.Share.Delete(link.Hash)
|
||||||
s = append(s[:i], s[i+1:]...)
|
s = append(s[:i], s[i+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJSON(w, r, s)
|
return renderJSON(w, r, s)
|
||||||
}
|
})
|
||||||
|
|
||||||
func (e *env) shareDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
var shareDeleteHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
user, ok := e.getUser(w, r)
|
hash := strings.TrimSuffix(r.URL.Path, "/")
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.Perm.Share {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := strings.TrimPrefix(r.URL.Path, apiSharePrefix)
|
|
||||||
hash = strings.TrimSuffix(hash, "/")
|
|
||||||
hash = strings.TrimPrefix(hash, "/")
|
hash = strings.TrimPrefix(hash, "/")
|
||||||
|
|
||||||
if hash == "" {
|
if hash == "" {
|
||||||
return
|
return http.StatusBadRequest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Share.Delete(hash)
|
err := d.store.Share.Delete(hash)
|
||||||
if err != nil {
|
return errToStatus(err), err
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
})
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) sharePostHandler(w http.ResponseWriter, r *http.Request) {
|
var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
path, ok := e.getShareData(w, r, apiSharePrefix)
|
var s *share.Link
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var s *lib.ShareLink
|
|
||||||
expire := r.URL.Query().Get("expires")
|
expire := r.URL.Query().Get("expires")
|
||||||
unit := r.URL.Query().Get("unit")
|
unit := r.URL.Query().Get("unit")
|
||||||
|
|
||||||
if expire == "" {
|
if expire == "" {
|
||||||
var err error
|
var err error
|
||||||
s, err = e.GetLinkPermanent(path)
|
s, err = d.store.Share.GetPermanent(r.URL.Path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
w.Write([]byte(e.GetSettings().BaseURL + "/share/" + s.Hash))
|
w.Write([]byte(d.settings.BaseURL + "/share/" + s.Hash))
|
||||||
return
|
return 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes := make([]byte, 6)
|
bytes := make([]byte, 6)
|
||||||
_, err := rand.Read(bytes)
|
_, err := rand.Read(bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
str := base64.URLEncoding.EncodeToString(bytes)
|
str := base64.URLEncoding.EncodeToString(bytes)
|
||||||
|
|
||||||
s = &lib.ShareLink{
|
s = &share.Link{
|
||||||
Path: path,
|
Path: r.URL.Path,
|
||||||
Hash: str,
|
Hash: str,
|
||||||
Expires: expire != "",
|
Expires: expire != "",
|
||||||
}
|
}
|
||||||
@ -117,8 +85,7 @@ func (e *env) sharePostHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if expire != "" {
|
if expire != "" {
|
||||||
num, err := strconv.Atoi(expire)
|
num, err := strconv.Atoi(expire)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var add time.Duration
|
var add time.Duration
|
||||||
@ -136,10 +103,9 @@ 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.SaveLink(s); err != nil {
|
if err := d.store.Share.Save(s); err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderJSON(w, r, s)
|
return renderJSON(w, r, s)
|
||||||
}
|
})
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
|
/*
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/GeertJohan/go.rice"
|
|
||||||
"github.com/filebrowser/filebrowser/auth"
|
"github.com/filebrowser/filebrowser/auth"
|
||||||
|
"github.com/filebrowser/filebrowser/storage"
|
||||||
|
"github.com/filebrowser/filebrowser/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *env) getStaticData() map[string]interface{} {
|
func getStaticData(storage *storage.Storage) (map[string]interface{}, error) {
|
||||||
e.RLockSettings()
|
settings, err := storage.Settings.Get()
|
||||||
defer e.RUnlockSettings()
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
settings := e.GetSettings()
|
}
|
||||||
|
|
||||||
staticURL := strings.TrimPrefix(settings.BaseURL+"/static", "/")
|
staticURL := strings.TrimPrefix(settings.BaseURL+"/static", "/")
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ func (e *env) getStaticData() map[string]interface{} {
|
|||||||
"Name": settings.Branding.Name,
|
"Name": settings.Branding.Name,
|
||||||
"DisableExternal": settings.Branding.DisableExternal,
|
"DisableExternal": settings.Branding.DisableExternal,
|
||||||
"BaseURL": settings.BaseURL,
|
"BaseURL": settings.BaseURL,
|
||||||
"Version": lib.Version,
|
"Version": version.Version,
|
||||||
"StaticURL": staticURL,
|
"StaticURL": staticURL,
|
||||||
"Signup": settings.Signup,
|
"Signup": settings.Signup,
|
||||||
"NoAuth": settings.AuthMethod == auth.MethodNoAuth,
|
"NoAuth": settings.AuthMethod == auth.MethodNoAuth,
|
||||||
@ -47,7 +47,12 @@ func (e *env) getStaticData() map[string]interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if settings.AuthMethod == auth.MethodJSONAuth {
|
if settings.AuthMethod == auth.MethodJSONAuth {
|
||||||
auther := e.Auther.(*auth.JSONAuth)
|
raw, err := storage.Auth.Get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
auther := raw.(*auth.JSONAuth)
|
||||||
|
|
||||||
if auther.ReCaptcha != nil {
|
if auther.ReCaptcha != nil {
|
||||||
data["ReCaptcha"] = auther.ReCaptcha.Key != "" && auther.ReCaptcha.Secret != ""
|
data["ReCaptcha"] = auther.ReCaptcha.Key != "" && auther.ReCaptcha.Secret != ""
|
||||||
@ -58,17 +63,21 @@ func (e *env) getStaticData() map[string]interface{} {
|
|||||||
|
|
||||||
b, _ := json.MarshalIndent(data, "", " ")
|
b, _ := json.MarshalIndent(data, "", " ")
|
||||||
data["Json"] = string(b)
|
data["Json"] = string(b)
|
||||||
|
return data, nil
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) getStaticHandlers() (http.Handler, http.Handler) {
|
func getStaticHandlers(storage *storage.Storage) (http.Handler, http.Handler, error) {
|
||||||
box := rice.MustFindBox("../frontend/dist")
|
box := rice.MustFindBox("../frontend/dist")
|
||||||
handler := http.FileServer(box.HTTPBox())
|
handler := http.FileServer(box.HTTPBox())
|
||||||
|
|
||||||
handleWithData := func(w http.ResponseWriter, r *http.Request, file string, contentType string) {
|
handleWithData := func(w http.ResponseWriter, r *http.Request, file string, contentType string) {
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
index := template.Must(template.New("index").Delims("[{[", "]}]").Parse(box.MustString(file)))
|
index := template.Must(template.New("index").Delims("[{[", "]}]").Parse(box.MustString(file)))
|
||||||
|
data, err := getStaticData(storage)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
err := index.Execute(w, e.getStaticData())
|
err := index.Execute(w, e.getStaticData())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -115,3 +124,5 @@ func (e *env) getStaticHandlers() (http.Handler, http.Handler) {
|
|||||||
|
|
||||||
return index, static
|
return index, static
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|||||||
122
http/users.go
122
http/users.go
@ -5,13 +5,17 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/errors"
|
||||||
"github.com/filebrowser/filebrowser/users"
|
"github.com/filebrowser/filebrowser/users"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type modifyUserRequest struct {
|
||||||
|
modifyRequest
|
||||||
|
Data *users.User `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
func getUserID(r *http.Request) (uint, error) {
|
func getUserID(r *http.Request) (uint, error) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
i, err := strconv.ParseUint(vars["id"], 10, 0)
|
i, err := strconv.ParseUint(vars["id"], 10, 0)
|
||||||
@ -21,47 +25,44 @@ func getUserID(r *http.Request) (uint, error) {
|
|||||||
return uint(i), err
|
return uint(i), err
|
||||||
}
|
}
|
||||||
|
|
||||||
type modifyUserRequest struct {
|
func getUser(w http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) {
|
||||||
modifyRequest
|
|
||||||
Data *users.User `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUser(w http.ResponseWriter, r *http.Request) (*modifyUserRequest, bool) {
|
|
||||||
if r.Body == nil {
|
if r.Body == nil {
|
||||||
httpErr(w, r, http.StatusBadRequest, nil)
|
return nil, errors.ErrEmptyRequest
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &modifyUserRequest{}
|
req := &modifyUserRequest{}
|
||||||
err := json.NewDecoder(r.Body).Decode(req)
|
err := json.NewDecoder(r.Body).Decode(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusBadRequest, err)
|
return nil, err
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.What != "user" {
|
if req.What != "user" {
|
||||||
httpErr(w, r, http.StatusBadRequest, nil)
|
return nil, errors.ErrInvalidDataType
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return req, true
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *env) usersGetHandler(w http.ResponseWriter, r *http.Request) {
|
func withSelfOrAdmin(fn handleFunc) handleFunc {
|
||||||
user, ok := e.getUser(w, r)
|
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
if !ok {
|
id, err := getUserID(r)
|
||||||
return
|
if err != nil {
|
||||||
}
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
if !user.Perm.Admin {
|
if d.user.ID != id && !d.user.Perm.Admin {
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
return http.StatusForbidden, nil
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
users, err := e.GetUsers()
|
d.raw = id
|
||||||
|
return fn(w, r, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
users, err := d.store.Users.Gets()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
@ -72,66 +73,33 @@ func (e *env) usersGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return users[i].ID < users[j].ID
|
return users[i].ID < users[j].ID
|
||||||
})
|
})
|
||||||
|
|
||||||
renderJSON(w, r, users)
|
return renderJSON(w, r, users)
|
||||||
}
|
})
|
||||||
|
|
||||||
func (e *env) userSelfOrAdmin(w http.ResponseWriter, r *http.Request) (*users.User, uint, bool) {
|
var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
user, ok := e.getUser(w, r)
|
u, err := d.store.Users.Get(d.raw.(uint))
|
||||||
if !ok {
|
if err == errors.ErrNotExist {
|
||||||
return nil, 0, false
|
return http.StatusNotFound, err
|
||||||
}
|
|
||||||
|
|
||||||
id, err := getUserID(r)
|
|
||||||
if err != nil {
|
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
return nil, 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.ID != id && !user.Perm.Admin {
|
|
||||||
httpErr(w, r, http.StatusForbidden, nil)
|
|
||||||
return nil, 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return user, id, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) userGetHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
_, id, ok := e.userSelfOrAdmin(w, r)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u, err := e.GetUser(id)
|
|
||||||
if err == lib.ErrNotExist {
|
|
||||||
httpErr(w, r, http.StatusNotFound, nil)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
return http.StatusInternalServerError, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Password = ""
|
u.Password = ""
|
||||||
renderJSON(w, r, u)
|
return renderJSON(w, r, u)
|
||||||
}
|
})
|
||||||
|
|
||||||
func (e *env) userDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
var userDeleteHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
_, id, ok := e.userSelfOrAdmin(w, r)
|
err := d.store.Users.Delete(d.raw.(uint))
|
||||||
if !ok {
|
if err == errors.ErrNotExist {
|
||||||
return
|
return http.StatusNotFound, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.DeleteUser(id)
|
return http.StatusOK, nil
|
||||||
if err == lib.ErrNotExist {
|
})
|
||||||
httpErr(w, r, http.StatusNotFound, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
/*
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *env) userPostHandler(w http.ResponseWriter, r *http.Request) {
|
func (e *env) userPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
_, ok := e.getAdminUser(w, r)
|
_, ok := e.getAdminUser(w, r)
|
||||||
@ -237,4 +205,4 @@ func (e *env) userPutHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
httpErr(w, r, http.StatusInternalServerError, err)
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|||||||
37
http/utils.go
Normal file
37
http/utils.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func renderJSON(w http.ResponseWriter, r *http.Request, data interface{}) (int, error) {
|
||||||
|
marsh, err := json.Marshal(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
if _, err := w.Write(marsh); err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func errToStatus(err error) int {
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
return http.StatusOK
|
||||||
|
case os.IsPermission(err):
|
||||||
|
return http.StatusForbidden
|
||||||
|
case os.IsNotExist(err):
|
||||||
|
return http.StatusNotFound
|
||||||
|
case os.IsExist(err):
|
||||||
|
return http.StatusConflict
|
||||||
|
default:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,19 +1,6 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
/*
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/search"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{
|
var upgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
@ -147,4 +134,4 @@ func (e *env) searchHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
httpErr(w, r, http.StatusInternalServerError, err)
|
httpErr(w, r, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|||||||
34
runner/parser.go
Normal file
34
runner/parser.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy"
|
||||||
|
"github.com/filebrowser/filebrowser/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseCommand parses the command taking in account if the current
|
||||||
|
// instance uses a shell to run the commands or just calls the binary
|
||||||
|
// directyly.
|
||||||
|
func ParseCommand(s *settings.Settings, raw string) ([]string, error) {
|
||||||
|
command := []string{}
|
||||||
|
|
||||||
|
if len(s.Shell) == 0 {
|
||||||
|
cmd, args, err := caddy.SplitCommandAndArgs(raw)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = exec.LookPath(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
command = append(command, cmd)
|
||||||
|
command = append(command, args...)
|
||||||
|
} else {
|
||||||
|
command = append(s.Shell, raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return command, nil
|
||||||
|
}
|
||||||
81
runner/runner.go
Normal file
81
runner/runner.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/settings"
|
||||||
|
"github.com/filebrowser/filebrowser/users"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Runner is a commands runner.
|
||||||
|
type Runner struct {
|
||||||
|
*settings.Settings
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunHook runs the hooks for the before and after event.
|
||||||
|
func (r *Runner) RunHook(fn func() error, evt, path, dst string, user *users.User) error {
|
||||||
|
path = user.FullPath(path)
|
||||||
|
dst = user.FullPath(dst)
|
||||||
|
|
||||||
|
if val, ok := r.Commands["before_"+evt]; ok {
|
||||||
|
for _, command := range val {
|
||||||
|
err := r.exec(command, "before_"+evt, path, dst, user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := fn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if val, ok := r.Commands["after_"+evt]; ok {
|
||||||
|
for _, command := range val {
|
||||||
|
err := r.exec(command, "after_"+evt, path, dst, user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runner) exec(raw, evt, path, dst string, user *users.User) error {
|
||||||
|
blocking := true
|
||||||
|
|
||||||
|
if strings.HasSuffix(raw, "&") {
|
||||||
|
blocking = false
|
||||||
|
raw = strings.TrimSpace(strings.TrimSuffix(raw, "&"))
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err := ParseCommand(r.Settings, raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(command[0], command[1:]...)
|
||||||
|
cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", evt))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("USERNAME=%s", user.Username))
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst))
|
||||||
|
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
if !blocking {
|
||||||
|
log.Printf("[INFO] Nonblocking Command: \"%s\"", strings.Join(command, " "))
|
||||||
|
return cmd.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] Blocking Command: \"%s\"", strings.Join(command, " "))
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user