feat: add sorting to user itself

License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
Henrique Dias 2018-12-29 20:02:35 +00:00
parent 1e163bce84
commit 9adb60206c
9 changed files with 159 additions and 97 deletions

@ -1 +1 @@
Subproject commit 82abdaf3900aae349f172cc1f5494fa5b6e72a02
Subproject commit 9977299d4e3dd201cd3bf648fb70b03e09d6e4e3

View File

@ -79,10 +79,11 @@ func (e *Env) signupHandler(w http.ResponseWriter, r *http.Request) {
}
type userInfo struct {
ID uint `json:"id"`
Locale string `json:"locale"`
ViewMode types.ViewMode `json:"viewMode"`
Perm types.Permissions `json:"perm"`
ID uint `json:"id"`
Locale string `json:"locale"`
ViewMode types.ViewMode `json:"viewMode"`
Perm types.Permissions `json:"perm"`
LockPassword bool `json:"lockPassword"`
}
type authToken struct {
@ -136,10 +137,11 @@ func (e *Env) auth(next http.HandlerFunc) http.HandlerFunc {
func (e *Env) printToken(w http.ResponseWriter, r *http.Request, user *types.User) {
claims := &authToken{
User: userInfo{
ID: user.ID,
Locale: user.Locale,
ViewMode: user.ViewMode,
Perm: user.Perm,
ID: user.ID,
Locale: user.Locale,
ViewMode: user.ViewMode,
Perm: user.Perm,
LockPassword: user.LockPassword,
},
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),

View File

@ -16,6 +16,11 @@ const (
keyUserID key = iota
)
type modifyRequest struct {
What string `json:"what"` // Answer to: what data type?
Which []string `json:"which"` // Answer to: which fields?
}
// Env ...
type Env struct {
Auther types.Auther

View File

@ -62,15 +62,7 @@ func (e *Env) resourceGetHandler(w http.ResponseWriter, r *http.Request) {
}
if file.IsDir {
scope := "/"
if sort, order, err := handleSortOrder(w, r, scope); err == nil {
file.Listing.Sort = sort
file.Listing.Order = order
} else {
httpErr(w, r, http.StatusBadRequest, err)
return
}
file.Listing.Sorting = user.Sorting
file.Listing.ApplySort()
renderJSON(w, r, file)
return
@ -240,42 +232,3 @@ func (e *Env) resourcePatchHandler(w http.ResponseWriter, r *http.Request) {
httpErr(w, r, httpFsErr(err), err)
}
func handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, err error) {
sort = r.URL.Query().Get("sort")
order = r.URL.Query().Get("order")
switch sort {
case "":
sort = "name"
if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil {
sort = sortCookie.Value
}
case "name", "size":
http.SetCookie(w, &http.Cookie{
Name: "sort",
Value: sort,
MaxAge: 31536000,
Path: scope,
Secure: r.TLS != nil,
})
}
switch order {
case "":
order = "asc"
if orderCookie, orderErr := r.Cookie("order"); orderErr == nil {
order = orderCookie.Value
}
case "asc", "desc":
http.SetCookie(w, &http.Cookie{
Name: "order",
Value: order,
MaxAge: 31536000,
Path: scope,
Secure: r.TLS != nil,
})
}
return
}

View File

@ -1,9 +1,11 @@
package http
import (
"encoding/json"
"net/http"
"sort"
"strconv"
"strings"
"github.com/filebrowser/filebrowser/types"
"github.com/gorilla/mux"
@ -18,6 +20,32 @@ func getUserID(r *http.Request) (uint, error) {
return uint(i), err
}
type modifyUserRequest struct {
modifyRequest
Data *types.User `json:"data"`
}
func getUser(w http.ResponseWriter, r *http.Request) (*modifyUserRequest, bool) {
if r.Body == nil {
httpErr(w, r, http.StatusBadRequest, nil)
return nil, false
}
req := &modifyUserRequest{}
err := json.NewDecoder(r.Body).Decode(req)
if err != nil {
httpErr(w, r, http.StatusBadRequest, err)
return nil, false
}
if req.What != "user" {
httpErr(w, r, http.StatusBadRequest, nil)
return nil, false
}
return req, true
}
func (e *Env) usersGetHandler(w http.ResponseWriter, r *http.Request) {
user, ok := e.getUser(w, r)
if !ok {
@ -109,5 +137,69 @@ func (e *Env) userPostHandler(w http.ResponseWriter, r *http.Request) {
}
func (e *Env) userPutHandler(w http.ResponseWriter, r *http.Request) {
// TODO: fill me
sessionUser, modifiedID, ok := e.userSelfOrAdmin(w, r)
if !ok {
return
}
req, ok := getUser(w, r)
if !ok {
return
}
if req.Data.ID != modifiedID {
httpErr(w, r, http.StatusBadRequest, nil)
return
}
var err error
if len(req.Which) == 1 && req.Which[0] == "all" {
if !sessionUser.Perm.Admin {
httpErr(w, r, http.StatusForbidden, nil)
return
}
if req.Data.Password != "" {
req.Data.Password, err = types.HashPwd(req.Data.Password)
} else {
var suser *types.User
suser, err = e.Store.Users.Get(modifiedID)
req.Data.Password = suser.Password
}
if err != nil {
httpErr(w, r, http.StatusInternalServerError, err)
return
}
req.Which = []string{}
}
for k, v := range req.Which {
if v == "password" {
if !sessionUser.Perm.Admin && sessionUser.LockPassword {
httpErr(w, r, http.StatusForbidden, nil)
return
}
req.Data.Password, err = types.HashPwd(req.Data.Password)
if err != nil {
httpErr(w, r, http.StatusInternalServerError, err)
return
}
}
if !sessionUser.Perm.Admin && (v == "scope" || v == "perm" || v == "username") {
httpErr(w, r, http.StatusForbidden, nil)
return
}
req.Which[k] = strings.Title(v)
}
err = e.Store.Users.Update(req.Data, req.Which...)
if err != nil {
httpErr(w, r, http.StatusInternalServerError, err)
}
}

View File

@ -11,15 +11,14 @@ type Listing struct {
Items []*File `json:"items"`
NumDirs int `json:"numDirs"`
NumFiles int `json:"numFiles"`
Sort string `json:"sort"`
Order string `json:"order"`
Sorting Sorting `json:"sorting"`
}
// ApplySort applies the sort order using .Order and .Sort
func (l Listing) ApplySort() {
// Check '.Order' to know how to sort
if l.Order == "desc" {
switch l.Sort {
if !l.Sorting.Asc {
switch l.Sorting.By {
case "name":
sort.Sort(sort.Reverse(byName(l)))
case "size":
@ -31,7 +30,7 @@ func (l Listing) ApplySort() {
return
}
} else { // If we had more Orderings we could add them here
switch l.Sort {
switch l.Sorting.By {
case "name":
sort.Sort(byName(l))
case "size":

View File

@ -2,14 +2,15 @@ package types
import (
"regexp"
"strings"
)
// Rule is a allow/disallow rule.
type Rule struct {
Regex bool
Allow bool
Path string
Regexp *Regexp
Regex bool `json:"regex"`
Allow bool `json:"allow"`
Path string `json:"path"`
Regexp *Regexp `json:"regexp"`
}
// Regexp is a wrapper to the native regexp type where we
@ -27,3 +28,17 @@ func (r *Regexp) MatchString(s string) bool {
return r.regexp.MatchString(s)
}
func isAllowed(path string, rules []Rule) bool {
for _, rule := range rules {
if rule.Regex {
if rule.Regexp.MatchString(path) {
return rule.Allow
}
} else if strings.HasPrefix(path, rule.Path) {
return rule.Allow
}
}
return true
}

View File

@ -10,7 +10,19 @@ type Settings struct {
Signup bool `json:"signup"`
Defaults UserDefaults `json:"defaults"`
AuthMethod AuthMethod `json:"authMethod"`
Branding Branding `json:"Branding"`
Branding Branding `json:"branding"`
Rules []Rule `json:"rules"` // TODO: use this add to cli
}
// IsAllowed matches the rules against the url.
func (e Settings) IsAllowed(url string) bool {
return isAllowed(url, e.Rules)
}
// Sorting contains a sorting order.
type Sorting struct {
By string `json:"by"`
Asc bool `json:"asc"`
}
// Branding contains the branding settings of the app.
@ -26,5 +38,6 @@ type UserDefaults struct {
Scope string `json:"scope"`
Locale string `json:"locale"`
ViewMode ViewMode `json:"viewMode"`
Sorting Sorting `json:"sorting"` // TODO: add to cli
Perm Permissions `json:"perm"`
}

View File

@ -1,8 +1,6 @@
package types
import (
"strings"
"github.com/spf13/afero"
"golang.org/x/crypto/bcrypt"
)
@ -30,15 +28,17 @@ type Permissions struct {
// User describes a user.
type User struct {
ID uint `storm:"id,increment" json:"id"`
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
Locale string `json:"locale"`
ViewMode ViewMode `json:"viewMode"`
Perm Permissions `json:"perm"`
Fs afero.Fs `json:"-"`
Rules []Rule `json:"rules"`
ID uint `storm:"id,increment" json:"id"`
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"` // TODO: add to cli
ViewMode ViewMode `json:"viewMode"`
Perm Permissions `json:"perm"`
Sorting Sorting `json:"sorting"` // TODO: add to cli
Fs afero.Fs `json:"-"`
Rules []Rule `json:"rules"`
}
// BuildFs builds the FileSystem property of the user,
@ -51,24 +51,7 @@ func (u *User) BuildFs() {
// IsAllowed checks if an user is allowed to go to a certain path.
func (u User) IsAllowed(url string) bool {
var rule *Rule
i := len(u.Rules) - 1
for i >= 0 {
rule = &u.Rules[i]
if rule.Regex {
if rule.Regexp.MatchString(url) {
return rule.Allow
}
} else if strings.HasPrefix(url, rule.Path) {
return rule.Allow
}
i--
}
return true
return isAllowed(url, u.Rules)
}
// HashPwd hashes a password.