feat: add sorting to user itself
License: MIT Signed-off-by: Henrique Dias <hacdias@gmail.com>
This commit is contained in:
parent
1e163bce84
commit
9adb60206c
2
frontend
2
frontend
@ -1 +1 @@
|
|||||||
Subproject commit 82abdaf3900aae349f172cc1f5494fa5b6e72a02
|
Subproject commit 9977299d4e3dd201cd3bf648fb70b03e09d6e4e3
|
||||||
@ -83,6 +83,7 @@ type userInfo struct {
|
|||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
ViewMode types.ViewMode `json:"viewMode"`
|
ViewMode types.ViewMode `json:"viewMode"`
|
||||||
Perm types.Permissions `json:"perm"`
|
Perm types.Permissions `json:"perm"`
|
||||||
|
LockPassword bool `json:"lockPassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type authToken struct {
|
type authToken struct {
|
||||||
@ -140,6 +141,7 @@ func (e *Env) printToken(w http.ResponseWriter, r *http.Request, user *types.Use
|
|||||||
Locale: user.Locale,
|
Locale: user.Locale,
|
||||||
ViewMode: user.ViewMode,
|
ViewMode: user.ViewMode,
|
||||||
Perm: user.Perm,
|
Perm: user.Perm,
|
||||||
|
LockPassword: user.LockPassword,
|
||||||
},
|
},
|
||||||
StandardClaims: jwt.StandardClaims{
|
StandardClaims: jwt.StandardClaims{
|
||||||
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
|
ExpiresAt: time.Now().Add(time.Hour * 24).Unix(),
|
||||||
|
|||||||
@ -16,6 +16,11 @@ const (
|
|||||||
keyUserID key = iota
|
keyUserID key = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type modifyRequest struct {
|
||||||
|
What string `json:"what"` // Answer to: what data type?
|
||||||
|
Which []string `json:"which"` // Answer to: which fields?
|
||||||
|
}
|
||||||
|
|
||||||
// Env ...
|
// Env ...
|
||||||
type Env struct {
|
type Env struct {
|
||||||
Auther types.Auther
|
Auther types.Auther
|
||||||
|
|||||||
@ -62,15 +62,7 @@ func (e *Env) resourceGetHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if file.IsDir {
|
if file.IsDir {
|
||||||
scope := "/"
|
file.Listing.Sorting = user.Sorting
|
||||||
|
|
||||||
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.ApplySort()
|
file.Listing.ApplySort()
|
||||||
renderJSON(w, r, file)
|
renderJSON(w, r, file)
|
||||||
return
|
return
|
||||||
@ -240,42 +232,3 @@ func (e *Env) resourcePatchHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
httpErr(w, r, httpFsErr(err), err)
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/types"
|
"github.com/filebrowser/filebrowser/types"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@ -18,6 +20,32 @@ func getUserID(r *http.Request) (uint, error) {
|
|||||||
return uint(i), err
|
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) {
|
func (e *Env) usersGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
user, ok := e.getUser(w, r)
|
user, ok := e.getUser(w, r)
|
||||||
if !ok {
|
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) {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,15 +11,14 @@ type Listing struct {
|
|||||||
Items []*File `json:"items"`
|
Items []*File `json:"items"`
|
||||||
NumDirs int `json:"numDirs"`
|
NumDirs int `json:"numDirs"`
|
||||||
NumFiles int `json:"numFiles"`
|
NumFiles int `json:"numFiles"`
|
||||||
Sort string `json:"sort"`
|
Sorting Sorting `json:"sorting"`
|
||||||
Order string `json:"order"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplySort applies the sort order using .Order and .Sort
|
// ApplySort applies the sort order using .Order and .Sort
|
||||||
func (l Listing) ApplySort() {
|
func (l Listing) ApplySort() {
|
||||||
// Check '.Order' to know how to sort
|
// Check '.Order' to know how to sort
|
||||||
if l.Order == "desc" {
|
if !l.Sorting.Asc {
|
||||||
switch l.Sort {
|
switch l.Sorting.By {
|
||||||
case "name":
|
case "name":
|
||||||
sort.Sort(sort.Reverse(byName(l)))
|
sort.Sort(sort.Reverse(byName(l)))
|
||||||
case "size":
|
case "size":
|
||||||
@ -31,7 +30,7 @@ func (l Listing) ApplySort() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else { // If we had more Orderings we could add them here
|
} else { // If we had more Orderings we could add them here
|
||||||
switch l.Sort {
|
switch l.Sorting.By {
|
||||||
case "name":
|
case "name":
|
||||||
sort.Sort(byName(l))
|
sort.Sort(byName(l))
|
||||||
case "size":
|
case "size":
|
||||||
|
|||||||
@ -2,14 +2,15 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rule is a allow/disallow rule.
|
// Rule is a allow/disallow rule.
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
Regex bool
|
Regex bool `json:"regex"`
|
||||||
Allow bool
|
Allow bool `json:"allow"`
|
||||||
Path string
|
Path string `json:"path"`
|
||||||
Regexp *Regexp
|
Regexp *Regexp `json:"regexp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regexp is a wrapper to the native regexp type where we
|
// 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)
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -10,7 +10,19 @@ type Settings struct {
|
|||||||
Signup bool `json:"signup"`
|
Signup bool `json:"signup"`
|
||||||
Defaults UserDefaults `json:"defaults"`
|
Defaults UserDefaults `json:"defaults"`
|
||||||
AuthMethod AuthMethod `json:"authMethod"`
|
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.
|
// Branding contains the branding settings of the app.
|
||||||
@ -26,5 +38,6 @@ type UserDefaults struct {
|
|||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
ViewMode ViewMode `json:"viewMode"`
|
ViewMode ViewMode `json:"viewMode"`
|
||||||
|
Sorting Sorting `json:"sorting"` // TODO: add to cli
|
||||||
Perm Permissions `json:"perm"`
|
Perm Permissions `json:"perm"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
@ -35,8 +33,10 @@ type User struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
Locale string `json:"locale"`
|
Locale string `json:"locale"`
|
||||||
|
LockPassword bool `json:"lockPassword"` // TODO: add to cli
|
||||||
ViewMode ViewMode `json:"viewMode"`
|
ViewMode ViewMode `json:"viewMode"`
|
||||||
Perm Permissions `json:"perm"`
|
Perm Permissions `json:"perm"`
|
||||||
|
Sorting Sorting `json:"sorting"` // TODO: add to cli
|
||||||
Fs afero.Fs `json:"-"`
|
Fs afero.Fs `json:"-"`
|
||||||
Rules []Rule `json:"rules"`
|
Rules []Rule `json:"rules"`
|
||||||
}
|
}
|
||||||
@ -51,24 +51,7 @@ func (u *User) BuildFs() {
|
|||||||
|
|
||||||
// IsAllowed checks if an user is allowed to go to a certain path.
|
// IsAllowed checks if an user is allowed to go to a certain path.
|
||||||
func (u User) IsAllowed(url string) bool {
|
func (u User) IsAllowed(url string) bool {
|
||||||
var rule *Rule
|
return isAllowed(url, u.Rules)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashPwd hashes a password.
|
// HashPwd hashes a password.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user