Skip to content

Commit

Permalink
feat: add global scope (filebrowser#604)
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Henrique Dias <[email protected]>

Former-commit-id: a44ebfc7a5687b5f51f0ff791335f66ab9f2e8e0 [formerly 9a044fadd8f2ebbb7dbb773273799c26a797f513] [formerly 7f374d016eaf756cfce215dd16a2274bbabe1915 [formerly f55f205]]
Former-commit-id: 31015ddc5f4fc28c895743f6fe9dcf5488bb4b01 [formerly e439027304a1e49667fafde011e07d043ef0d2ee]
Former-commit-id: 0394c60358673b56991364260b1cbe41fa457593
  • Loading branch information
hacdias authored Jan 6, 2019
1 parent f2d952b commit f1a89f5
Show file tree
Hide file tree
Showing 18 changed files with 60 additions and 81 deletions.
5 changes: 2 additions & 3 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package auth
import (
"net/http"

"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users"
)

// Auther is the authentication interface.
type Auther interface {
// Auth is called to authenticate a request.
Auth(*http.Request) (*users.User, error)
// SetStorage attaches the Storage instance.
SetStorage(*users.Storage)
Auth(*http.Request, *users.Storage, *settings.Settings) (*users.User, error)
}
10 changes: 2 additions & 8 deletions auth/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ type jsonCred struct {
// JSONAuth is a json implementaion of an Auther.
type JSONAuth struct {
ReCaptcha *ReCaptcha
storage *users.Storage
}

// Auth authenticates the user via a json in content body.
func (a *JSONAuth) Auth(r *http.Request) (*users.User, error) {
func (a *JSONAuth) Auth(r *http.Request, sto *users.Storage, set *settings.Settings) (*users.User, error) {
var cred jsonCred

if r.Body == nil {
Expand All @@ -52,19 +51,14 @@ func (a *JSONAuth) Auth(r *http.Request) (*users.User, error) {
}
}

u, err := a.storage.Get(cred.Username)
u, err := sto.Get(set.Scope, cred.Username)
if err != nil || !users.CheckPwd(cred.Password, u.Password) {
return nil, os.ErrPermission
}

return u, nil
}

// SetStorage attaches the storage to the auther.
func (a *JSONAuth) SetStorage(s *users.Storage) {
a.storage = s
}

const reCaptchaAPI = "/recaptcha/api/siteverify"

// ReCaptcha identifies a recaptcha conenction.
Expand Down
10 changes: 2 additions & 8 deletions auth/none.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@ const MethodNoAuth settings.AuthMethod = "noauth"

// NoAuth is no auth implementation of auther.
type NoAuth struct {
storage *users.Storage
}

// Auth uses authenticates user 1.
func (a *NoAuth) Auth(r *http.Request) (*users.User, error) {
return a.storage.Get(1)
}

// SetStorage attaches the storage to the auther.
func (a *NoAuth) SetStorage(s *users.Storage) {
a.storage = s
func (a *NoAuth) Auth(r *http.Request, sto *users.Storage, set *settings.Settings) (*users.User, error) {
return sto.Get(set.Scope, 1)
}
12 changes: 3 additions & 9 deletions auth/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,16 @@ const MethodProxyAuth settings.AuthMethod = "proxy"

// ProxyAuth is a proxy implementation of an auther.
type ProxyAuth struct {
Header string
storage *users.Storage
Header string
}

// Auth authenticates the user via an HTTP header.
func (a *ProxyAuth) Auth(r *http.Request) (*users.User, error) {
func (a *ProxyAuth) Auth(r *http.Request, sto *users.Storage, set *settings.Settings) (*users.User, error) {
username := r.Header.Get(a.Header)
user, err := a.storage.Get(username)
user, err := sto.Get(set.Scope, username)
if err == errors.ErrNotExist {
return nil, os.ErrPermission
}

return user, err
}

// SetStorage attaches the storage to the auther.
func (a *ProxyAuth) SetStorage(s *users.Storage) {
a.storage = s
}
10 changes: 2 additions & 8 deletions auth/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,9 @@ func NewStorage(back StorageBackend, users *users.Storage) *Storage {
return &Storage{back: back, users: users}
}

// Get wraps a StorageBackend.Get and calls SetStorage on the auther.
// Get wraps a StorageBackend.Get.
func (s *Storage) Get(t settings.AuthMethod) (Auther, error) {
auther, err := s.back.Get(t)
if err != nil {
return nil, err
}

auther.SetStorage(s.users)
return auther, nil
return s.back.Get(t)
}

// Save wraps a StorageBackend.Save.
Expand Down
1 change: 0 additions & 1 deletion cmd/config_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ func init() {
configCmd.AddCommand(configInitCmd)
rootCmd.AddCommand(configInitCmd)
addConfigFlags(configInitCmd)
configInitCmd.MarkFlagRequired("scope")
}

var configInitCmd = &cobra.Command{
Expand Down
20 changes: 12 additions & 8 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package cmd

import (
"crypto/tls"
"errors"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path/filepath"
"strconv"

"github.com/asdine/storm"
Expand All @@ -32,7 +32,7 @@ func init() {
rootCmd.Flags().IntP("port", "p", 8080, "port to listen on")
rootCmd.Flags().StringP("cert", "c", "", "tls certificate")
rootCmd.Flags().StringP("key", "k", "", "tls key")
rootCmd.Flags().StringP("scope", "s", "", "root scope to which user's scope are relative too")
rootCmd.Flags().StringP("scope", "s", ".", "scope to prepend to a user's scope when it is relative")
}

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -76,6 +76,15 @@ func serveAndListen(cmd *cobra.Command, args []string) {
address := mustGetString(cmd, "address")
cert := mustGetString(cmd, "cert")
key := mustGetString(cmd, "key")
scope := mustGetString(cmd, "scope")

scope, err := filepath.Abs(scope)
checkErr(err)
settings, err := st.Settings.Get()
checkErr(err)
settings.Scope = scope
err = st.Settings.Save(settings)
checkErr(err)

handler, err := fbhttp.NewHandler(st)
checkErr(err)
Expand All @@ -100,11 +109,6 @@ func serveAndListen(cmd *cobra.Command, args []string) {
}

func quickSetup(cmd *cobra.Command) {
scope := mustGetString(cmd, "scope")
if scope == "" {
panic(errors.New("scope flag must be set for quick setup"))
}

db, err := storm.Open(databasePath)
checkErr(err)
defer db.Close()
Expand All @@ -115,7 +119,7 @@ func quickSetup(cmd *cobra.Command) {
Signup: false,
AuthMethod: auth.MethodJSONAuth,
Defaults: settings.UserDefaults{
Scope: scope,
Scope: ".",
Locale: "en",
Perm: users.Permissions{
Admin: false,
Expand Down
2 changes: 1 addition & 1 deletion cmd/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func runRules(cmd *cobra.Command, users func(*users.User, *storage.Storage), glo

id := getUserIdentifier(cmd)
if id != nil {
user, err := st.Users.Get(id)
user, err := st.Users.Get("", id)
checkErr(err)

if users != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func addUserFlags(cmd *cobra.Command) {
cmd.Flags().Bool("sorting.asc", false, "sorting by ascending order")
cmd.Flags().Bool("lockPassword", false, "lock password")
cmd.Flags().StringSlice("commands", nil, "a list of the commands a user can execute")
cmd.Flags().String("scope", "", "scope for users")
cmd.Flags().String("scope", ".", "scope for users")
cmd.Flags().String("locale", "en", "locale for users")
cmd.Flags().String("viewMode", string(users.ListViewMode), "view mode for users")
}
Expand Down
10 changes: 6 additions & 4 deletions cmd/users_find.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ var findUsers = func(cmd *cobra.Command, args []string) {
defer db.Close()
st := getStorage(db)

settings, err := st.Settings.Get()
checkErr(err)

username, _ := cmd.Flags().GetString("username")
id, _ := cmd.Flags().GetUint("id")

var err error
var list []*users.User
var user *users.User

if username != "" {
user, err = st.Users.Get(username)
user, err = st.Users.Get(settings.Scope, username)
} else if id != 0 {
user, err = st.Users.Get(id)
user, err = st.Users.Get(settings.Scope, id)
} else {
list, err = st.Users.Gets()
list, err = st.Users.Gets(settings.Scope)
}

checkErr(err)
Expand Down
8 changes: 5 additions & 3 deletions cmd/users_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ options you want to change.`,
defer db.Close()
st := getStorage(db)

set, err := st.Settings.Get()
checkErr(err)

id, _ := cmd.Flags().GetUint("id")
username := mustGetString(cmd, "username")
password := mustGetString(cmd, "password")

var user *users.User
var err error

if id != 0 {
user, err = st.Users.Get(id)
user, err = st.Users.Get(set.Scope, id)
} else {
user, err = st.Users.Get(username)
user, err = st.Users.Get(set.Scope, username)
}

checkErr(err)
Expand Down
4 changes: 2 additions & 2 deletions http/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func withUser(fn handleFunc) handleFunc {
w.Header().Add("X-Renew-Token", "true")
}

d.user, err = d.store.Users.Get(tk.User.ID)
d.user, err = d.store.Users.Get(d.settings.Scope, tk.User.ID)
if err != nil {
return http.StatusInternalServerError, err
}
Expand All @@ -91,7 +91,7 @@ var loginHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, e
return http.StatusInternalServerError, err
}

user, err := auther.Auth(r)
user, err := auther.Auth(r, d.store.Users, d.Settings)
if err == os.ErrPermission {
return http.StatusForbidden, nil
} else if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion http/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var withHashFile = func(fn handleFunc) handleFunc {
return errToStatus(err), err
}

user, err := d.store.Users.Get(link.UserID)
user, err := d.store.Users.Get(d.settings.Scope, link.UserID)
if err != nil {
return errToStatus(err), err
}
Expand Down
6 changes: 3 additions & 3 deletions http/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func withSelfOrAdmin(fn handleFunc) handleFunc {
}

var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
users, err := d.store.Users.Gets()
users, err := d.store.Users.Gets(d.settings.Scope)
if err != nil {
return http.StatusInternalServerError, err
}
Expand All @@ -78,7 +78,7 @@ var usersGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
})

var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
u, err := d.store.Users.Get(d.raw.(uint))
u, err := d.store.Users.Get(d.settings.Scope, d.raw.(uint))
if err == errors.ErrNotExist {
return http.StatusNotFound, err
}
Expand Down Expand Up @@ -147,7 +147,7 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
req.Data.Password, err = users.HashPwd(req.Data.Password)
} else {
var suser *users.User
suser, err = d.store.Users.Get(d.raw.(uint))
suser, err = d.store.Users.Get(d.settings.Scope, d.raw.(uint))
req.Data.Password = suser.Password
}

Expand Down
1 change: 1 addition & 0 deletions settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type AuthMethod string
type Settings struct {
Key []byte `json:"key"`
BaseURL string `json:"baseURL"`
Scope string `json:"scope"`
Signup bool `json:"signup"`
Defaults UserDefaults `json:"defaults"`
AuthMethod AuthMethod `json:"authMethod"`
Expand Down
9 changes: 1 addition & 8 deletions storage/bolt/importer/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package importer
import (
"encoding/json"
"fmt"
"path/filepath"

"github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/rules"
Expand Down Expand Up @@ -52,7 +51,6 @@ func readOldUsers(db *storm.DB) ([]*oldUser, error) {
}

func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
var err error
list := []*users.User{}

for _, oldUser := range old {
Expand Down Expand Up @@ -82,12 +80,7 @@ func convertUsersToNew(old []*oldUser) ([]*users.User, error) {
user.Rules = append(user.Rules, *rule)
}

user.Scope, err = filepath.Abs(user.Scope)
if err != nil {
return nil, err
}

err = user.Clean()
err := user.Clean("")
if err != nil {
return nil, err
}
Expand Down
12 changes: 6 additions & 6 deletions users/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewStorage(back StorageBackend) *Storage {
// Get allows you to get a user by its name or username. The provided
// id must be a string for username lookup or a uint for id lookup. If id
// is neither, a ErrInvalidDataType will be returned.
func (s *Storage) Get(id interface{}) (*User, error) {
func (s *Storage) Get(baseScope string, id interface{}) (*User, error) {
var (
user *User
err error
Expand All @@ -55,27 +55,27 @@ func (s *Storage) Get(id interface{}) (*User, error) {
return nil, err
}

user.Clean()
user.Clean(baseScope)
return user, err
}

// Gets gets a list of all users.
func (s *Storage) Gets() ([]*User, error) {
func (s *Storage) Gets(baseScope string) ([]*User, error) {
users, err := s.back.Gets()
if err != nil {
return nil, err
}

for _, user := range users {
user.Clean()
user.Clean(baseScope)
}

return users, err
}

// Update updates a user in the database.
func (s *Storage) Update(user *User, fields ...string) error {
err := user.Clean(fields...)
err := user.Clean("", fields...)
if err != nil {
return err
}
Expand All @@ -93,7 +93,7 @@ func (s *Storage) Update(user *User, fields ...string) error {

// Save saves the user in a storage.
func (s *Storage) Save(user *User) error {
if err := user.Clean(); err != nil {
if err := user.Clean(""); err != nil {
return err
}

Expand Down
Loading

0 comments on commit f1a89f5

Please sign in to comment.