refactor: add more go linters (#970)

This commit is contained in:
Oleg Lobanov 2020-06-01 01:12:36 +02:00 committed by GitHub
parent 54d92a2708
commit 700f32718e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 436 additions and 221 deletions

View File

@ -2,10 +2,10 @@ version: 2
jobs: jobs:
lint: lint:
docker: docker:
- image: golangci/golangci-lint:v1.16 - image: golangci/golangci-lint:v1.27.0
steps: steps:
- checkout - checkout
- run: golangci-lint run -v -D errcheck - run: golangci-lint run -v
build-node: build-node:
docker: docker:
- image: circleci/node - image: circleci/node

132
.golangci.yml Normal file
View File

@ -0,0 +1,132 @@
linters-settings:
dupl:
threshold: 100
exhaustive:
default-signifies-exhaustive: false
funlen:
lines: 100
statements: 50
goconst:
min-len: 2
min-occurrences: 2
gocritic:
enabled-tags:
- diagnostic
- experimental
- opinionated
- performance
- style
disabled-checks:
- dupImport # https://github.com/go-critic/go-critic/issues/845
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
min-complexity: 15
goimports:
local-prefixes: github.com/filebrowser/filebrowser
golint:
min-confidence: 0
gomnd:
settings:
mnd:
# don't include the "operation" and "assign"
checks: argument,case,condition,return
govet:
check-shadowing: true
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
nolintlint:
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
require-explanation: false # don't require an explanation for nolint directives
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
- errcheck
- funlen
- gochecknoinits
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- golint
- gomnd
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- nolintlint
- rowserrcheck
- scopelint
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace
- prealloc
# don't enable:
# - asciicheck
# - exhaustive (TODO: enable after next release; current release at time of writing is v1.27)
# - gochecknoglobals
# - gocognit
# - godot
# - godox
# - goerr113
# - maligned
# - nestif
# - testpackage
# - wsl
issues:
exclude-rules:
- path: cmd/.*.go
linters:
- gochecknoinits
- path: .*_test.go
linters:
- lll
- gochecknoinits
- gocyclo
- funlen
- dupl
- scopelint
- text: "Auther"
linters:
- misspell
run:
skip-dirs:
- frontend/
skip-files:
- http/rice-box.go
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.27.x # use the fixed version to not introduce new linters unexpectedly

View File

@ -20,7 +20,7 @@ type jsonCred struct {
ReCaptcha string `json:"recaptcha"` ReCaptcha string `json:"recaptcha"`
} }
// JSONAuth is a json implementaion of an Auther. // JSONAuth is a json implementation of an Auther.
type JSONAuth struct { type JSONAuth struct {
ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"` ReCaptcha *ReCaptcha `json:"recaptcha" yaml:"recaptcha"`
} }
@ -40,7 +40,7 @@ func (a JSONAuth) Auth(r *http.Request, sto *users.Storage, root string) (*users
// If ReCaptcha is enabled, check the code. // If ReCaptcha is enabled, check the code.
if a.ReCaptcha != nil && len(a.ReCaptcha.Secret) > 0 { if a.ReCaptcha != nil && len(a.ReCaptcha.Secret) > 0 {
ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) ok, err := a.ReCaptcha.Ok(cred.ReCaptcha) //nolint:shadow
if err != nil { if err != nil {
return nil, err return nil, err
@ -66,7 +66,7 @@ func (a JSONAuth) LoginPage() bool {
const reCaptchaAPI = "/recaptcha/api/siteverify" const reCaptchaAPI = "/recaptcha/api/siteverify"
// ReCaptcha identifies a recaptcha conenction. // ReCaptcha identifies a recaptcha connection.
type ReCaptcha struct { type ReCaptcha struct {
Host string `json:"host"` Host string `json:"host"`
Key string `json:"key"` Key string `json:"key"`
@ -89,6 +89,7 @@ func (r *ReCaptcha) Ok(response string) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return false, nil return false, nil

View File

@ -18,8 +18,8 @@ type Storage struct {
} }
// NewStorage creates a auth storage from a backend. // NewStorage creates a auth storage from a backend.
func NewStorage(back StorageBackend, users *users.Storage) *Storage { func NewStorage(back StorageBackend, userStore *users.Storage) *Storage {
return &Storage{back: back, users: users} return &Storage{back: back, users: userStore}
} }
// Get wraps a StorageBackend.Get. // Get wraps a StorageBackend.Get.

View File

@ -14,7 +14,7 @@ var cmdsAddCmd = &cobra.Command{
Use: "add <event> <command>", Use: "add <event> <command>",
Short: "Add a command to run on a specific event", Short: "Add a command to run on a specific event",
Long: `Add a command to run on a specific event.`, Long: `Add a command to run on a specific event.`,
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2), //nolint:mnd
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := d.store.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)

View File

@ -23,7 +23,7 @@ You can also specify an optional parameter (index_end) so
you can remove all commands from 'index' to 'index_end', you can remove all commands from 'index' to 'index_end',
including 'index_end'.`, including 'index_end'.`,
Args: func(cmd *cobra.Command, args []string) error { Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { if err := cobra.RangeArgs(2, 3)(cmd, args); err != nil { //nolint:mnd
return err return err
} }
@ -43,7 +43,7 @@ including 'index_end'.`,
i, err := strconv.Atoi(args[1]) i, err := strconv.Atoi(args[1])
checkErr(err) checkErr(err)
f := i f := i
if len(args) == 3 { if len(args) == 3 { //nolint:mnd
f, err = strconv.Atoi(args[2]) f, err = strconv.Atoi(args[2])
checkErr(err) checkErr(err)
} }

View File

@ -8,11 +8,12 @@ import (
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
func init() { func init() {
@ -44,6 +45,7 @@ func addConfigFlags(flags *pflag.FlagSet) {
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links") flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
} }
//nolint:gocyclo
func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.AuthMethod, auth.Auther) { func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.AuthMethod, auth.Auther) {
method := settings.AuthMethod(mustGetString(flags, "auth.method")) method := settings.AuthMethod(mustGetString(flags, "auth.method"))
@ -53,11 +55,12 @@ func getAuthentication(flags *pflag.FlagSet, defaults ...interface{}) (settings.
for _, arg := range defaults { for _, arg := range defaults {
switch def := arg.(type) { switch def := arg.(type) {
case *settings.Settings: case *settings.Settings:
method = settings.AuthMethod(def.AuthMethod) method = def.AuthMethod
case auth.Auther: case auth.Auther:
ms, err := json.Marshal(def) ms, err := json.Marshal(def)
checkErr(err) checkErr(err)
json.Unmarshal(ms, &defaultAuther) err = json.Unmarshal(ms, &defaultAuther)
checkErr(err)
} }
} }
} }

View File

@ -6,9 +6,10 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/spf13/cobra"
) )
func init() { func init() {
@ -55,7 +56,7 @@ The path must be for a json or yaml file.`,
checkErr(err) checkErr(err)
var rawAuther interface{} var rawAuther interface{}
if filepath.Ext(args[0]) != ".json" { if filepath.Ext(args[0]) != ".json" { //nolint:goconst
rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{})) rawAuther = cleanUpInterfaceMap(file.Auther.(map[interface{}]interface{}))
} else { } else {
rawAuther = file.Auther rawAuther = file.Auther

View File

@ -4,8 +4,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/settings"
) )
func init() { func init() {

View File

@ -88,7 +88,7 @@ func generateMarkdown(cmd *cobra.Command, w io.Writer) {
short := cmd.Short short := cmd.Short
long := cmd.Long long := cmd.Long
if len(long) == 0 { if long == "" {
long = short long = short
} }
@ -106,21 +106,21 @@ func generateMarkdown(cmd *cobra.Command, w io.Writer) {
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example)) buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example))
} }
printOptions(buf, cmd, name) printOptions(buf, cmd)
_, err := buf.WriteTo(w) _, err := buf.WriteTo(w)
checkErr(err) checkErr(err)
} }
func generateFlagsTable(fs *pflag.FlagSet, buf io.StringWriter) { func generateFlagsTable(fs *pflag.FlagSet, buf io.StringWriter) {
buf.WriteString("| Name | Shorthand | Usage |\n") _, _ = buf.WriteString("| Name | Shorthand | Usage |\n")
buf.WriteString("|------|-----------|-------|\n") _, _ = buf.WriteString("|------|-----------|-------|\n")
fs.VisitAll(func(f *pflag.Flag) { fs.VisitAll(func(f *pflag.Flag) {
buf.WriteString("|" + f.Name + "|" + f.Shorthand + "|" + f.Usage + "|\n") _, _ = buf.WriteString("|" + f.Name + "|" + f.Shorthand + "|" + f.Usage + "|\n")
}) })
} }
func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) { func printOptions(buf *bytes.Buffer, cmd *cobra.Command) {
flags := cmd.NonInheritedFlags() flags := cmd.NonInheritedFlags()
flags.SetOutput(buf) flags.SetOutput(buf)
if flags.HasAvailableFlags() { if flags.HasAvailableFlags() {

View File

@ -3,8 +3,9 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/users"
) )
func init() { func init() {

View File

@ -13,16 +13,17 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/filebrowser/filebrowser/v2/auth"
fbhttp "github.com/filebrowser/filebrowser/v2/http"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
v "github.com/spf13/viper" v "github.com/spf13/viper"
lumberjack "gopkg.in/natefinch/lumberjack.v2" lumberjack "gopkg.in/natefinch/lumberjack.v2"
"github.com/filebrowser/filebrowser/v2/auth"
fbhttp "github.com/filebrowser/filebrowser/v2/http"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users"
) )
var ( var (
@ -113,16 +114,17 @@ user created with the credentials from options "username" and "password".`,
var listener net.Listener var listener net.Listener
if server.Socket != "" { switch {
case server.Socket != "":
listener, err = net.Listen("unix", server.Socket) listener, err = net.Listen("unix", server.Socket)
checkErr(err) checkErr(err)
} else if server.TLSKey != "" && server.TLSCert != "" { case server.TLSKey != "" && server.TLSCert != "":
cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) cer, err := tls.LoadX509KeyPair(server.TLSCert, server.TLSKey) //nolint:shadow
checkErr(err) checkErr(err)
listener, err = tls.Listen("tcp", adr, &tls.Config{Certificates: []tls.Certificate{cer}}) listener, err = tls.Listen("tcp", adr, &tls.Config{Certificates: []tls.Certificate{cer}}) //nolint:shadow
checkErr(err) checkErr(err)
} else { default:
listener, err = net.Listen("tcp", adr) listener, err = net.Listen("tcp", adr) //nolint:shadow
checkErr(err) checkErr(err)
} }
@ -142,13 +144,14 @@ user created with the credentials from options "username" and "password".`,
}, pythonConfig{allowNoDB: true}), }, pythonConfig{allowNoDB: true}),
} }
func cleanupHandler(listener net.Listener, c chan os.Signal) { func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfacer
sig := <-c sig := <-c
log.Printf("Caught signal %s: shutting down.", sig) log.Printf("Caught signal %s: shutting down.", sig)
listener.Close() listener.Close()
os.Exit(0) os.Exit(0)
} }
//nolint:gocyclo
func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server { func getRunParams(flags *pflag.FlagSet, st *storage.Storage) *settings.Server {
server, err := st.Settings.GetServer() server, err := st.Settings.GetServer()
checkErr(err) checkErr(err)
@ -348,5 +351,4 @@ func initConfig() {
} else { } else {
cfgFile = "Using config file: " + v.ConfigFileUsed() cfgFile = "Using config file: " + v.ConfigFileUsed()
} }
} }

View File

@ -3,15 +3,16 @@ package cmd
import ( import (
"strconv" "strconv"
"github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra"
) )
func init() { func init() {
rulesCmd.AddCommand(rulesRmCommand) rulesCmd.AddCommand(rulesRmCommand)
rulesRmCommand.Flags().Uint("index", 0, "index of rule to remove") rulesRmCommand.Flags().Uint("index", 0, "index of rule to remove")
rulesRmCommand.MarkFlagRequired("index") _ = rulesRmCommand.MarkFlagRequired("index")
} }
var rulesRmCommand = &cobra.Command{ var rulesRmCommand = &cobra.Command{
@ -43,7 +44,7 @@ including 'index_end'.`,
i, err := strconv.Atoi(args[0]) i, err := strconv.Atoi(args[0])
checkErr(err) checkErr(err)
f := i f := i
if len(args) == 2 { if len(args) == 2 { //nolint:mnd
f, err = strconv.Atoi(args[1]) f, err = strconv.Atoi(args[1])
checkErr(err) checkErr(err)
} }

View File

@ -3,12 +3,13 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
func init() { func init() {
@ -18,8 +19,8 @@ func init() {
} }
var rulesCmd = &cobra.Command{ var rulesCmd = &cobra.Command{
Use: "rules", Use: "rules",
Short: "Rules management utility", Short: "Rules management utility",
Long: `On each subcommand you'll have available at least two flags: Long: `On each subcommand you'll have available at least two flags:
"username" and "id". You must either set only one of them "username" and "id". You must either set only one of them
or none. If you set one of them, the command will apply to or none. If you set one of them, the command will apply to
@ -28,14 +29,14 @@ rules.`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
} }
func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User), global func(*settings.Settings)) { func runRules(st *storage.Storage, cmd *cobra.Command, usersFn func(*users.User), globalFn func(*settings.Settings)) {
id := getUserIdentifier(cmd.Flags()) id := getUserIdentifier(cmd.Flags())
if id != nil { if id != nil {
user, err := st.Users.Get("", id) user, err := st.Users.Get("", id)
checkErr(err) checkErr(err)
if users != nil { if usersFn != nil {
users(user) usersFn(user)
} }
printRules(user.Rules, id) printRules(user.Rules, id)
@ -45,8 +46,8 @@ func runRules(st *storage.Storage, cmd *cobra.Command, users func(*users.User),
s, err := st.Settings.Get() s, err := st.Settings.Get()
checkErr(err) checkErr(err)
if global != nil { if globalFn != nil {
global(s) globalFn(s)
} }
printRules(s.Rules, id) printRules(s.Rules, id)
@ -65,14 +66,14 @@ func getUserIdentifier(flags *pflag.FlagSet) interface{} {
return nil return nil
} }
func printRules(rules []rules.Rule, id interface{}) { func printRules(rulez []rules.Rule, id interface{}) {
if id == nil { if id == nil {
fmt.Printf("Global Rules:\n\n") fmt.Printf("Global Rules:\n\n")
} else { } else {
fmt.Printf("Rules for user %v:\n\n", id) fmt.Printf("Rules for user %v:\n\n", id)
} }
for id, rule := range rules { for id, rule := range rulez {
fmt.Printf("(%d) ", id) fmt.Printf("(%d) ", id)
if rule.Regex { if rule.Regex {
if rule.Allow { if rule.Allow {

View File

@ -3,10 +3,11 @@ package cmd
import ( import (
"regexp" "regexp"
"github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra"
) )
func init() { func init() {

View File

@ -1,8 +1,9 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/storage/bolt/importer"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/storage/bolt/importer"
) )
func init() { func init() {
@ -10,7 +11,7 @@ func init() {
upgradeCmd.Flags().String("old.database", "", "") upgradeCmd.Flags().String("old.database", "", "")
upgradeCmd.Flags().String("old.config", "", "") upgradeCmd.Flags().String("old.config", "", "")
upgradeCmd.MarkFlagRequired("old.database") _ = upgradeCmd.MarkFlagRequired("old.database")
} }
var upgradeCmd = &cobra.Command{ var upgradeCmd = &cobra.Command{

View File

@ -7,10 +7,11 @@ import (
"strconv" "strconv"
"text/tabwriter" "text/tabwriter"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users"
) )
func init() { func init() {
@ -24,38 +25,38 @@ var usersCmd = &cobra.Command{
Args: cobra.NoArgs, Args: cobra.NoArgs,
} }
func printUsers(users []*users.User) { func printUsers(usrs []*users.User) {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock") fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
for _, user := range users { for _, u := range usrs {
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n", fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
user.ID, u.ID,
user.Username, u.Username,
user.Scope, u.Scope,
user.Locale, u.Locale,
user.ViewMode, u.ViewMode,
user.Perm.Admin, u.Perm.Admin,
user.Perm.Execute, u.Perm.Execute,
user.Perm.Create, u.Perm.Create,
user.Perm.Rename, u.Perm.Rename,
user.Perm.Modify, u.Perm.Modify,
user.Perm.Delete, u.Perm.Delete,
user.Perm.Share, u.Perm.Share,
user.Perm.Download, u.Perm.Download,
user.LockPassword, u.LockPassword,
) )
} }
w.Flush() w.Flush()
} }
func parseUsernameOrID(arg string) (string, uint) { func parseUsernameOrID(arg string) (username string, id uint) {
id, err := strconv.ParseUint(arg, 10, 0) id64, err := strconv.ParseUint(arg, 10, 0)
if err != nil { if err != nil {
return arg, 0 return arg, 0
} }
return "", uint(id) return "", uint(id64)
} }
func addUserFlags(flags *pflag.FlagSet) { func addUserFlags(flags *pflag.FlagSet) {
@ -84,6 +85,7 @@ func getViewMode(flags *pflag.FlagSet) users.ViewMode {
return viewMode return viewMode
} }
//nolint:gocyclo
func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all bool) { func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all bool) {
visit := func(flag *pflag.Flag) { visit := func(flag *pflag.Flag) {
switch flag.Name { switch flag.Name {

View File

@ -1,8 +1,9 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/users"
) )
func init() { func init() {
@ -14,7 +15,7 @@ var usersAddCmd = &cobra.Command{
Use: "add <username> <password>", Use: "add <username> <password>",
Short: "Create a new user", Short: "Create a new user",
Long: `Create a new user and add it to the database.`, Long: `Create a new user and add it to the database.`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2), //nolint:mnd
Run: python(func(cmd *cobra.Command, args []string, d pythonData) { Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
s, err := d.store.Settings.Get() s, err := d.store.Settings.Get()
checkErr(err) checkErr(err)
@ -33,9 +34,9 @@ var usersAddCmd = &cobra.Command{
servSettings, err := d.store.Settings.GetServer() servSettings, err := d.store.Settings.GetServer()
checkErr(err) checkErr(err)
//since getUserDefaults() polluted s.Defaults.Scope // since getUserDefaults() polluted s.Defaults.Scope
//which makes the Scope not the one saved in the db // which makes the Scope not the one saved in the db
//we need the right s.Defaults.Scope here // we need the right s.Defaults.Scope here
s2, err := d.store.Settings.Get() s2, err := d.store.Settings.Get()
checkErr(err) checkErr(err)

View File

@ -1,8 +1,9 @@
package cmd package cmd
import ( import (
"github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/users"
) )
func init() { func init() {

View File

@ -2,11 +2,13 @@ package cmd
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"strconv" "strconv"
"github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/users"
) )
func init() { func init() {
@ -65,8 +67,7 @@ list or set it to 0.`,
// with the new username. If there is, print an error and cancel the // with the new username. If there is, print an error and cancel the
// operation // operation
if user.Username != onDB.Username { if user.Username != onDB.Username {
conflictuous, err := d.store.Users.Get("", user.Username) if conflictuous, err := d.store.Users.Get("", user.Username); err == nil { //nolint:shadow
if err == nil {
checkErr(usernameConflictError(user.Username, conflictuous.ID, user.ID)) checkErr(usernameConflictError(user.Username, conflictuous.ID, user.ID))
} }
} }
@ -82,6 +83,7 @@ list or set it to 0.`,
}, pythonConfig{}), }, pythonConfig{}),
} }
func usernameConflictError(username string, original, new uint) error { func usernameConflictError(username string, originalID, newID uint) error {
return errors.New("can't import user with ID " + strconv.Itoa(int(new)) + " and username \"" + username + "\" because the username is already registred with the user " + strconv.Itoa(int(original))) return fmt.Errorf(`can't import user with ID %d and username "%s" because the username is already registred with the user %d`,
newID, username, originalID)
} }

View File

@ -1,9 +1,10 @@
package cmd package cmd
import ( import (
"github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/spf13/cobra"
) )
func init() { func init() {

View File

@ -9,12 +9,13 @@ import (
"path/filepath" "path/filepath"
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/storage/bolt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/storage/bolt"
) )
func checkErr(err error) { func checkErr(err error) {
@ -70,7 +71,9 @@ func dbExists(path string) (bool, error) {
d := filepath.Dir(path) d := filepath.Dir(path)
_, err = os.Stat(d) _, err = os.Stat(d)
if os.IsNotExist(err) { if os.IsNotExist(err) {
os.MkdirAll(d, 0700) if err := os.MkdirAll(d, 0700); err != nil { //nolint:shadow
return false, err
}
return false, nil return false, nil
} }
} }
@ -113,7 +116,7 @@ func marshal(filename string, data interface{}) error {
encoder := json.NewEncoder(fd) encoder := json.NewEncoder(fd)
encoder.SetIndent("", " ") encoder.SetIndent("", " ")
return encoder.Encode(data) return encoder.Encode(data)
case ".yml", ".yaml": case ".yml", ".yaml": //nolint:goconst
encoder := yaml.NewEncoder(fd) encoder := yaml.NewEncoder(fd)
return encoder.Encode(data) return encoder.Encode(data)
default: default:

View File

@ -3,8 +3,9 @@ package cmd
import ( import (
"fmt" "fmt"
"github.com/filebrowser/filebrowser/v2/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/filebrowser/filebrowser/v2/version"
) )
func init() { func init() {

View File

@ -1,8 +1,8 @@
package files package files
import ( import (
"crypto/md5" "crypto/md5" //nolint:gosec
"crypto/sha1" "crypto/sha1" //nolint:gosec
"crypto/sha256" "crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
@ -17,9 +17,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/spf13/afero"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/spf13/afero"
) )
// FileInfo describes a file. // FileInfo describes a file.
@ -74,7 +75,10 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
if opts.Expand { if opts.Expand {
if file.IsDir { if file.IsDir {
return file, file.readListing(opts.Checker) if err := file.readListing(opts.Checker); err != nil { //nolint:shadow
return nil, err
}
return file, nil
} }
err = file.detectType(opts.Modify, true) err = file.detectType(opts.Modify, true)
@ -105,6 +109,7 @@ func (i *FileInfo) Checksum(algo string) error {
var h hash.Hash var h hash.Hash
//nolint:gosec
switch algo { switch algo {
case "md5": case "md5":
h = md5.New() h = md5.New()
@ -127,6 +132,8 @@ func (i *FileInfo) Checksum(algo string) error {
return nil return nil
} }
//nolint:goconst
//TODO: use constants
func (i *FileInfo) detectType(modify, saveContent bool) error { func (i *FileInfo) detectType(modify, saveContent bool) error {
// failing to detect the type should not return error. // failing to detect the type should not return error.
// imagine the situation where a file in a dir with thousands // imagine the situation where a file in a dir with thousands
@ -198,9 +205,9 @@ func (i *FileInfo) detectSubtitles() {
// TODO: detect multiple languages. Base.Lang.vtt // TODO: detect multiple languages. Base.Lang.vtt
path := strings.TrimSuffix(i.Path, ext) + ".vtt" fPath := strings.TrimSuffix(i.Path, ext) + ".vtt"
if _, err := i.Fs.Stat(path); err == nil { if _, err := i.Fs.Stat(fPath); err == nil {
i.Subtitles = append(i.Subtitles, path) i.Subtitles = append(i.Subtitles, fPath)
} }
} }
@ -219,16 +226,16 @@ func (i *FileInfo) readListing(checker rules.Checker) error {
for _, f := range dir { for _, f := range dir {
name := f.Name() name := f.Name()
path := path.Join(i.Path, name) fPath := path.Join(i.Path, name)
if !checker.Check(path) { if !checker.Check(fPath) {
continue continue
} }
if strings.HasPrefix(f.Mode().String(), "L") { if strings.HasPrefix(f.Mode().String(), "L") {
// It's a symbolic link. We try to follow it. If it doesn't work, // It's a symbolic link. We try to follow it. If it doesn't work,
// we stay with the link information instead if the target's. // we stay with the link information instead if the target's.
info, err := i.Fs.Stat(path) info, err := i.Fs.Stat(fPath)
if err == nil { if err == nil {
f = info f = info
} }
@ -242,7 +249,7 @@ func (i *FileInfo) readListing(checker rules.Checker) error {
Mode: f.Mode(), Mode: f.Mode(),
IsDir: f.IsDir(), IsDir: f.IsDir(),
Extension: filepath.Ext(name), Extension: filepath.Ext(name),
Path: path, Path: fPath,
} }
if file.IsDir { if file.IsDir {

View File

@ -16,8 +16,10 @@ type Listing struct {
} }
// ApplySort applies the sort order using .Order and .Sort // ApplySort applies the sort order using .Order and .Sort
//nolint:goconst
func (l Listing) ApplySort() { func (l Listing) ApplySort() {
// Check '.Order' to know how to sort // Check '.Order' to know how to sort
// TODO: use enum
if !l.Sorting.Asc { if !l.Sorting.Asc {
switch l.Sorting.By { switch l.Sorting.By {
case "name": case "name":

View File

@ -4,41 +4,45 @@ import (
"unicode/utf8" "unicode/utf8"
) )
func isBinary(content []byte, n int) bool { func isBinary(content []byte, _ int) bool {
maybeStr := string(content) maybeStr := string(content)
runeCnt := utf8.RuneCount(content) runeCnt := utf8.RuneCount(content)
runeIndex := 0 runeIndex := 0
gotRuneErrCnt := 0 gotRuneErrCnt := 0
firstRuneErrIndex := -1 firstRuneErrIndex := -1
for _, b := range maybeStr { const (
// 8 and below are control chars (e.g. backspace, null, eof, etc) // 8 and below are control chars (e.g. backspace, null, eof, etc)
if b <= 8 { maxControlCharsCode = 8
// 0xFFFD(65533) is the "error" Rune or "Unicode replacement character"
// see https://golang.org/pkg/unicode/utf8/#pkg-constants
unicodeReplacementChar = 0xFFFD
)
for _, b := range maybeStr {
if b <= maxControlCharsCode {
return true return true
} }
// 0xFFFD(65533) is the "error" Rune or "Unicode replacement character" if b == unicodeReplacementChar {
// see https://golang.org/pkg/unicode/utf8/#pkg-constants // if it is not the last (utf8.UTFMax - x) rune
if b == 0xFFFD {
//if it is not the last (utf8.UTFMax - x) rune
if runeCnt > utf8.UTFMax && runeIndex < runeCnt-utf8.UTFMax { if runeCnt > utf8.UTFMax && runeIndex < runeCnt-utf8.UTFMax {
return true return true
} else { }
//else it is the last (utf8.UTFMax - x) rune // else it is the last (utf8.UTFMax - x) rune
//there maybe Vxxx, VVxx, VVVx, thus, we may got max 3 0xFFFD rune (asume V is the byte we got) // there maybe Vxxx, VVxx, VVVx, thus, we may got max 3 0xFFFD rune (assume V is the byte we got)
//for Chinese, it can only be Vxx, VVx, we may got max 2 0xFFFD rune // for Chinese, it can only be Vxx, VVx, we may got max 2 0xFFFD rune
gotRuneErrCnt++ gotRuneErrCnt++
//mark the first time // mark the first time
if firstRuneErrIndex == -1 { if firstRuneErrIndex == -1 {
firstRuneErrIndex = runeIndex firstRuneErrIndex = runeIndex
}
} }
} }
runeIndex++ runeIndex++
} }
//if last (utf8.UTFMax - x ) rune has the "error" Rune, but not all // if last (utf8.UTFMax - x ) rune has the "error" Rune, but not all
if firstRuneErrIndex != -1 && gotRuneErrCnt != runeCnt-firstRuneErrIndex { if firstRuneErrIndex != -1 && gotRuneErrCnt != runeCnt-firstRuneErrIndex {
return true return true
} }

View File

@ -9,7 +9,7 @@ import (
// CopyDir copies a directory from source to dest and all // CopyDir copies a directory from source to dest and all
// of its sub-directories. It doesn't stop if it finds an error // of its sub-directories. It doesn't stop if it finds an error
// during the copy. Returns an error if any. // during the copy. Returns an error if any.
func CopyDir(fs afero.Fs, source string, dest string) error { func CopyDir(fs afero.Fs, source, dest string) error {
// Get properties of source. // Get properties of source.
srcinfo, err := fs.Stat(source) srcinfo, err := fs.Stat(source)
if err != nil { if err != nil {

View File

@ -9,7 +9,7 @@ import (
// CopyFile copies a file from source to dest and returns // CopyFile copies a file from source to dest and returns
// an error if any. // an error if any.
func CopyFile(fs afero.Fs, source string, dest string) error { func CopyFile(fs afero.Fs, source, dest string) error {
// Open the source file. // Open the source file.
src, err := fs.Open(source) src, err := fs.Open(source)
if err != nil { if err != nil {

View File

@ -10,10 +10,15 @@ import (
jwt "github.com/dgrijalva/jwt-go" jwt "github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request" "github.com/dgrijalva/jwt-go/request"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
) )
const (
TokenExpirationTime = time.Hour * 2
)
type userInfo struct { type userInfo struct {
ID uint `json:"id"` ID uint `json:"id"`
Locale string `json:"locale"` Locale string `json:"locale"`
@ -161,7 +166,7 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data
return printToken(w, r, d, d.user) return printToken(w, r, d, d.user)
}) })
func printToken(w http.ResponseWriter, r *http.Request, d *data, user *users.User) (int, error) { func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) {
claims := &authToken{ claims := &authToken{
User: userInfo{ User: userInfo{
ID: user.ID, ID: user.ID,
@ -173,7 +178,7 @@ func printToken(w http.ResponseWriter, r *http.Request, d *data, user *users.Use
}, },
StandardClaims: jwt.StandardClaims{ StandardClaims: jwt.StandardClaims{
IssuedAt: time.Now().Unix(), IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(time.Hour * 2).Unix(), ExpiresAt: time.Now().Add(TokenExpirationTime).Unix(),
Issuer: "File Browser", Issuer: "File Browser",
}, },
} }
@ -185,6 +190,8 @@ func printToken(w http.ResponseWriter, r *http.Request, d *data, user *users.Use
} }
w.Header().Set("Content-Type", "cty") w.Header().Set("Content-Type", "cty")
w.Write([]byte(signed)) if _, err := w.Write([]byte(signed)); err != nil {
return http.StatusInternalServerError, err
}
return 0, nil return 0, nil
} }

View File

@ -9,8 +9,13 @@ import (
"strings" "strings"
"time" "time"
"github.com/filebrowser/filebrowser/v2/runner"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/filebrowser/filebrowser/v2/runner"
)
const (
WSWriteDeadline = 10 * time.Second
) )
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
@ -22,12 +27,14 @@ var (
cmdNotAllowed = []byte("Command not allowed.") cmdNotAllowed = []byte("Command not allowed.")
) )
func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) { func wsErr(ws *websocket.Conn, r *http.Request, status int, err error) { //nolint:unparam
txt := http.StatusText(status) txt := http.StatusText(status)
if err != nil || status >= 400 { if err != nil || status >= 400 {
log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err) log.Printf("%s: %v %s %v", r.URL.Path, status, r.RemoteAddr, err)
} }
ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(10*time.Second)) if err := ws.WriteControl(websocket.CloseInternalServerErr, []byte(txt), time.Now().Add(WSWriteDeadline)); err != nil { //nolint:shadow
log.Print(err)
}
} }
var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
@ -40,7 +47,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
var raw string var raw string
for { for {
_, msg, err := conn.ReadMessage() _, msg, err := conn.ReadMessage() //nolint:shadow
if err != nil { if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err) wsErr(conn, r, http.StatusInternalServerError, err)
return 0, nil return 0, nil
@ -53,8 +60,7 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
} }
if !d.user.CanExecute(strings.Split(raw, " ")[0]) { if !d.user.CanExecute(strings.Split(raw, " ")[0]) {
err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed) if err := conn.WriteMessage(websocket.TextMessage, cmdNotAllowed); err != nil { //nolint:shadow
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err) wsErr(conn, r, http.StatusInternalServerError, err)
} }
@ -63,15 +69,13 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
command, err := runner.ParseCommand(d.settings, raw) command, err := runner.ParseCommand(d.settings, raw)
if err != nil { if err != nil {
err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())) if err := conn.WriteMessage(websocket.TextMessage, []byte(err.Error())); err != nil { //nolint:shadow
if err != nil {
wsErr(conn, r, http.StatusInternalServerError, err) wsErr(conn, r, http.StatusInternalServerError, err)
} }
return 0, nil return 0, nil
} }
cmd := exec.Command(command[0], command[1:]...) cmd := exec.Command(command[0], command[1:]...) //nolint:gosec
cmd.Dir = d.user.FullPath(r.URL.Path) cmd.Dir = d.user.FullPath(r.URL.Path)
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
@ -93,7 +97,9 @@ var commandsHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *d
s := bufio.NewScanner(io.MultiReader(stdout, stderr)) s := bufio.NewScanner(io.MultiReader(stdout, stderr))
for s.Scan() { for s.Scan() {
conn.WriteMessage(websocket.TextMessage, s.Bytes()) if err := conn.WriteMessage(websocket.TextMessage, s.Bytes()); err != nil {
log.Print(err)
}
} }
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {

View File

@ -41,9 +41,9 @@ func (d *data) Check(path string) bool {
return true return true
} }
func handle(fn handleFunc, prefix string, storage *storage.Storage, server *settings.Server) http.Handler { func handle(fn handleFunc, prefix string, store *storage.Storage, server *settings.Server) http.Handler {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
settings, err := storage.Settings.Get() settings, err := store.Settings.Get()
if err != nil { if err != nil {
log.Fatalln("ERROR: couldn't get settings") log.Fatalln("ERROR: couldn't get settings")
return return
@ -51,7 +51,7 @@ func handle(fn handleFunc, prefix string, storage *storage.Storage, server *sett
status, err := fn(w, r, &data{ status, err := fn(w, r, &data{
Runner: &runner.Runner{Settings: settings}, Runner: &runner.Runner{Settings: settings},
store: storage, store: store,
settings: settings, settings: settings,
server: server, server: server,
}) })

View File

@ -3,9 +3,10 @@ package http
import ( import (
"net/http" "net/http"
"github.com/gorilla/mux"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"
"github.com/gorilla/mux"
) )
type modifyRequest struct { type modifyRequest struct {
@ -13,11 +14,11 @@ type modifyRequest struct {
Which []string `json:"which"` // Answer to: which fields? Which []string `json:"which"` // Answer to: which fields?
} }
func NewHandler(storage *storage.Storage, server *settings.Server) (http.Handler, error) { func NewHandler(store *storage.Storage, server *settings.Server) (http.Handler, error) {
server.Clean() server.Clean()
r := mux.NewRouter() r := mux.NewRouter()
index, static := getStaticHandlers(storage, server) index, static := getStaticHandlers(store, server)
// NOTE: This fixes the issue where it would redirect if people did not put a // NOTE: This fixes the issue where it would redirect if people did not put a
// trailing slash in the end. I hate this decision since this allows some awful // trailing slash in the end. I hate this decision since this allows some awful
@ -25,7 +26,7 @@ func NewHandler(storage *storage.Storage, server *settings.Server) (http.Handler
r = r.SkipClean(true) r = r.SkipClean(true)
monkey := func(fn handleFunc, prefix string) http.Handler { monkey := func(fn handleFunc, prefix string) http.Handler {
return handle(fn, prefix, storage, server) return handle(fn, prefix, store, server)
} }
r.PathPrefix("/static").Handler(static) r.PathPrefix("/static").Handler(static)

View File

@ -46,7 +46,7 @@ func ifPathWithName(r *http.Request) string {
pathElements := strings.Split(r.URL.Path, "/") pathElements := strings.Split(r.URL.Path, "/")
// prevent maliciously constructed parameters like `/api/public/dl/XZzCDnK2_not_exists_hash_name` // prevent maliciously constructed parameters like `/api/public/dl/XZzCDnK2_not_exists_hash_name`
// len(pathElements) will be 1, and golang will panic `runtime error: index out of range` // len(pathElements) will be 1, and golang will panic `runtime error: index out of range`
if len(pathElements) < 2 { if len(pathElements) < 2 { //nolint: mnd
return r.URL.Path return r.URL.Path
} }
id := pathElements[len(pathElements)-2] id := pathElements[len(pathElements)-2]

View File

@ -7,34 +7,37 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/users"
"github.com/hacdias/fileutils" "github.com/hacdias/fileutils"
"github.com/mholt/archiver" "github.com/mholt/archiver"
"github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/users"
) )
func parseQueryFiles(r *http.Request, f *files.FileInfo, u *users.User) ([]string, error) { func parseQueryFiles(r *http.Request, f *files.FileInfo, _ *users.User) ([]string, error) {
files := []string{} var fileSlice []string
names := strings.Split(r.URL.Query().Get("files"), ",") names := strings.Split(r.URL.Query().Get("files"), ",")
if len(names) == 0 { if len(names) == 0 {
files = append(files, f.Path) fileSlice = append(fileSlice, f.Path)
} else { } else {
for _, name := range names { for _, name := range names {
name, err := url.QueryUnescape(strings.Replace(name, "+", "%2B", -1)) name, err := url.QueryUnescape(strings.Replace(name, "+", "%2B", -1)) //nolint:shadow
if err != nil { if err != nil {
return nil, err return nil, err
} }
name = fileutils.SlashClean(name) name = fileutils.SlashClean(name)
files = append(files, filepath.Join(f.Path, name)) fileSlice = append(fileSlice, filepath.Join(f.Path, name))
} }
} }
return files, nil return fileSlice, nil
} }
//nolint: goconst
func parseQueryAlgorithm(r *http.Request) (string, archiver.Writer, error) { func parseQueryAlgorithm(r *http.Request) (string, archiver.Writer, error) {
// TODO: use enum
switch r.URL.Query().Get("algo") { switch r.URL.Query().Get("algo") {
case "zip", "true", "": case "zip", "true", "":
return ".zip", archiver.NewZip(), nil return ".zip", archiver.NewZip(), nil

View File

@ -74,7 +74,7 @@ var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Reques
} }
defer func() { defer func() {
io.Copy(ioutil.Discard, r.Body) _, _ = io.Copy(ioutil.Discard, r.Body)
}() }()
// For directories, only allow POST for creation. // For directories, only allow POST for creation.
@ -119,6 +119,7 @@ var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Reques
return errToStatus(err), err return errToStatus(err), err
}) })
//nolint: goconst
var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
src := r.URL.Path src := r.URL.Path
dst := r.URL.Query().Get("destination") dst := r.URL.Query().Get("destination")
@ -134,6 +135,7 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
} }
switch action { switch action {
// TODO: use enum
case "copy": case "copy":
if !d.user.Perm.Create { if !d.user.Perm.Create {
return http.StatusForbidden, nil return http.StatusForbidden, nil

View File

@ -57,7 +57,9 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
var err error var err error
s, err = d.store.Share.GetPermanent(r.URL.Path, d.user.ID) s, err = d.store.Share.GetPermanent(r.URL.Path, d.user.ID)
if err == nil { if err == nil {
w.Write([]byte(path.Join(d.server.BaseURL, "/share/", s.Hash))) if _, err := w.Write([]byte(path.Join(d.server.BaseURL, "/share/", s.Hash))); err != nil {
return http.StatusInternalServerError, err
}
return 0, nil return 0, nil
} }
} }

View File

@ -11,13 +11,14 @@ import (
"text/template" "text/template"
rice "github.com/GeertJohan/go.rice" rice "github.com/GeertJohan/go.rice"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/version" "github.com/filebrowser/filebrowser/v2/version"
) )
func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, box *rice.Box, file, contentType string) (int, error) { func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, box *rice.Box, file, contentType string) (int, error) {
w.Header().Set("Content-Type", contentType) w.Header().Set("Content-Type", contentType)
auther, err := d.store.Auth.Get(d.settings.AuthMethod) auther, err := d.store.Auth.Get(d.settings.AuthMethod)
@ -41,8 +42,8 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, box *
} }
if d.settings.Branding.Files != "" { if d.settings.Branding.Files != "" {
path := filepath.Join(d.settings.Branding.Files, "custom.css") fPath := filepath.Join(d.settings.Branding.Files, "custom.css")
_, err := os.Stat(path) _, err := os.Stat(fPath) //nolint:shadow
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
log.Printf("couldn't load custom styles: %v", err) log.Printf("couldn't load custom styles: %v", err)
@ -54,7 +55,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, box *
} }
if d.settings.AuthMethod == auth.MethodJSONAuth { if d.settings.AuthMethod == auth.MethodJSONAuth {
raw, err := d.store.Auth.Get(d.settings.AuthMethod) raw, err := d.store.Auth.Get(d.settings.AuthMethod) //nolint:shadow
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
@ -84,29 +85,29 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, d *data, box *
return 0, nil return 0, nil
} }
func getStaticHandlers(storage *storage.Storage, server *settings.Server) (http.Handler, http.Handler) { func getStaticHandlers(store *storage.Storage, server *settings.Server) (index, static http.Handler) {
box := rice.MustFindBox("../frontend/dist") box := rice.MustFindBox("../frontend/dist")
handler := http.FileServer(box.HTTPBox()) handler := http.FileServer(box.HTTPBox())
index := handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { index = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
return http.StatusNotFound, nil return http.StatusNotFound, nil
} }
w.Header().Set("x-xss-protection", "1; mode=block") w.Header().Set("x-xss-protection", "1; mode=block")
return handleWithStaticData(w, r, d, box, "index.html", "text/html; charset=utf-8") return handleWithStaticData(w, r, d, box, "index.html", "text/html; charset=utf-8")
}, "", storage, server) }, "", store, server)
static := handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
return http.StatusNotFound, nil return http.StatusNotFound, nil
} }
if d.settings.Branding.Files != "" { if d.settings.Branding.Files != "" {
if strings.HasPrefix(r.URL.Path, "img/") { if strings.HasPrefix(r.URL.Path, "img/") {
path := filepath.Join(d.settings.Branding.Files, r.URL.Path) fPath := filepath.Join(d.settings.Branding.Files, r.URL.Path)
if _, err := os.Stat(path); err == nil { if _, err := os.Stat(fPath); err == nil {
http.ServeFile(w, r, path) http.ServeFile(w, r, fPath)
return 0, nil return 0, nil
} }
} else if r.URL.Path == "custom.css" && d.settings.Branding.Files != "" { } else if r.URL.Path == "custom.css" && d.settings.Branding.Files != "" {
@ -121,7 +122,7 @@ func getStaticHandlers(storage *storage.Storage, server *settings.Server) (http.
} }
return handleWithStaticData(w, r, d, box, r.URL.Path, "application/javascript; charset=utf-8") return handleWithStaticData(w, r, d, box, r.URL.Path, "application/javascript; charset=utf-8")
}, "/static/", storage, server) }, "/static/", store, server)
return index, static return index, static
} }

View File

@ -8,9 +8,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/gorilla/mux"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
"github.com/gorilla/mux"
) )
type modifyUserRequest struct { type modifyUserRequest struct {
@ -27,7 +28,7 @@ func getUserID(r *http.Request) (uint, error) {
return uint(i), err return uint(i), err
} }
func getUser(w http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) { func getUser(_ http.ResponseWriter, r *http.Request) (*modifyUserRequest, error) {
if r.Body == nil { if r.Body == nil {
return nil, errors.ErrEmptyRequest return nil, errors.ErrEmptyRequest
} }

View File

@ -10,7 +10,7 @@ import (
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
) )
func renderJSON(w http.ResponseWriter, r *http.Request, data interface{}) (int, error) { func renderJSON(w http.ResponseWriter, _ *http.Request, data interface{}) (int, error) {
marsh, err := json.Marshal(data) marsh, err := json.Marshal(data)
if err != nil { if err != nil {

View File

@ -3,15 +3,16 @@ package runner
import ( import (
"os/exec" "os/exec"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/caddyserver/caddy" "github.com/caddyserver/caddy"
"github.com/filebrowser/filebrowser/v2/settings"
) )
// ParseCommand parses the command taking in account if the current // ParseCommand parses the command taking in account if the current
// instance uses a shell to run the commands or just calls the binary // instance uses a shell to run the commands or just calls the binary
// directyly. // directyly.
func ParseCommand(s *settings.Settings, raw string) ([]string, error) { func ParseCommand(s *settings.Settings, raw string) ([]string, error) {
command := []string{} var command []string
if len(s.Shell) == 0 { if len(s.Shell) == 0 {
cmd, args, err := caddy.SplitCommandAndArgs(raw) cmd, args, err := caddy.SplitCommandAndArgs(raw)
@ -27,7 +28,7 @@ func ParseCommand(s *settings.Settings, raw string) ([]string, error) {
command = append(command, cmd) command = append(command, cmd)
command = append(command, args...) command = append(command, args...)
} else { } else {
command = append(s.Shell, raw) command = append(s.Shell, raw) //nolint:gocritic
} }
return command, nil return command, nil

View File

@ -60,9 +60,9 @@ func (r *Runner) exec(raw, evt, path, dst string, user *users.User) error {
return err return err
} }
cmd := exec.Command(command[0], command[1:]...) cmd := exec.Command(command[0], command[1:]...) //nolint:gosec
cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path)) 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("SCOPE=%s", user.Scope)) //nolint:gocritic
cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", evt)) 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("USERNAME=%s", user.Username))
cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst)) cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst))

View File

@ -4,8 +4,9 @@ import (
"os" "os"
"strings" "strings"
"github.com/filebrowser/filebrowser/v2/rules"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/filebrowser/filebrowser/v2/rules"
) )
type searchOptions struct { type searchOptions struct {

View File

@ -17,21 +17,21 @@ var (
) )
// MakeUserDir makes the user directory according to settings. // MakeUserDir makes the user directory according to settings.
func (settings *Settings) MakeUserDir(username, userScope, serverRoot string) (string, error) { func (s *Settings) MakeUserDir(username, userScope, serverRoot string) (string, error) {
var err error var err error
userScope = strings.TrimSpace(userScope) userScope = strings.TrimSpace(userScope)
if userScope == "" || userScope == "./" { if userScope == "" || userScope == "./" {
userScope = "." userScope = "."
} }
if !settings.CreateUserDir { if !s.CreateUserDir {
return userScope, nil return userScope, nil
} }
fs := afero.NewBasePathFs(afero.NewOsFs(), serverRoot) fs := afero.NewBasePathFs(afero.NewOsFs(), serverRoot)
// Use the default auto create logic only if specific scope is not the default scope // Use the default auto create logic only if specific scope is not the default scope
if userScope != settings.Defaults.Scope { if userScope != s.Defaults.Scope {
// Try create the dir, for example: settings.Defaults.Scope == "." and userScope == "./foo" // Try create the dir, for example: settings.Defaults.Scope == "." and userScope == "./foo"
if userScope != "." { if userScope != "." {
err = fs.MkdirAll(userScope, os.ModePerm) err = fs.MkdirAll(userScope, os.ModePerm)
@ -50,7 +50,7 @@ func (settings *Settings) MakeUserDir(username, userScope, serverRoot string) (s
} }
// Create default user dir // Create default user dir
userHomeBase := settings.Defaults.Scope + string(os.PathSeparator) + "users" userHomeBase := s.Defaults.Scope + string(os.PathSeparator) + "users"
userHome := userHomeBase + string(os.PathSeparator) + username userHome := userHomeBase + string(os.PathSeparator) + username
err = fs.MkdirAll(userHome, os.ModePerm) err = fs.MkdirAll(userHome, os.ModePerm)
if err != nil { if err != nil {

View File

@ -33,7 +33,9 @@ func (s *Storage) GetByHash(hash string) (*Link, error) {
} }
if link.Expire != 0 && link.Expire <= time.Now().Unix() { if link.Expire != 0 && link.Expire <= time.Now().Unix() {
s.Delete(link.Hash) if err := s.Delete(link.Hash); err != nil {
return nil, err
}
return nil, errors.ErrNotExist return nil, errors.ErrNotExist
} }
@ -55,7 +57,9 @@ func (s *Storage) Gets(path string, id uint) ([]*Link, error) {
for i, link := range links { for i, link := range links {
if link.Expire != 0 && link.Expire <= time.Now().Unix() { if link.Expire != 0 && link.Expire <= time.Now().Unix() {
s.Delete(link.Hash) if err := s.Delete(link.Hash); err != nil {
return nil, err
}
links = append(links[:i], links[i+1:]...) links = append(links[:i], links[i+1:]...)
} }
} }

View File

@ -2,6 +2,7 @@ package bolt
import ( import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"

View File

@ -2,6 +2,7 @@ package bolt
import ( import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/auth" "github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/share" "github.com/filebrowser/filebrowser/v2/share"
@ -11,10 +12,10 @@ import (
// NewStorage creates a storage.Storage based on Bolt DB. // NewStorage creates a storage.Storage based on Bolt DB.
func NewStorage(db *storm.DB) (*storage.Storage, error) { func NewStorage(db *storm.DB) (*storage.Storage, error) {
users := users.NewStorage(usersBackend{db: db}) userStore := users.NewStorage(usersBackend{db: db})
share := share.NewStorage(shareBackend{db: db}) shareStore := share.NewStorage(shareBackend{db: db})
settings := settings.NewStorage(settingsBackend{db: db}) settingsStore := settings.NewStorage(settingsBackend{db: db})
auth := auth.NewStorage(authBackend{db: db}, users) authStore := auth.NewStorage(authBackend{db: db}, userStore)
err := save(db, "version", 2) err := save(db, "version", 2)
if err != nil { if err != nil {
@ -22,9 +23,9 @@ func NewStorage(db *storm.DB) (*storage.Storage, error) {
} }
return &storage.Storage{ return &storage.Storage{
Auth: auth, Auth: authStore,
Users: users, Users: userStore,
Share: share, Share: shareStore,
Settings: settings, Settings: settingsStore,
}, nil }, nil
} }

View File

@ -2,6 +2,7 @@ package bolt
import ( import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/settings" "github.com/filebrowser/filebrowser/v2/settings"
) )
@ -10,12 +11,12 @@ type settingsBackend struct {
} }
func (s settingsBackend) Get() (*settings.Settings, error) { func (s settingsBackend) Get() (*settings.Settings, error) {
settings := &settings.Settings{} set := &settings.Settings{}
return settings, get(s.db, "settings", settings) return set, get(s.db, "settings", set)
} }
func (s settingsBackend) Save(settings *settings.Settings) error { func (s settingsBackend) Save(set *settings.Settings) error {
return save(s.db, "settings", settings) return save(s.db, "settings", set)
} }
func (s settingsBackend) GetServer() (*settings.Server, error) { func (s settingsBackend) GetServer() (*settings.Server, error) {

View File

@ -7,14 +7,14 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/users"
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
toml "github.com/pelletier/go-toml" toml "github.com/pelletier/go-toml"
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
"github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users"
) )
type oldDefs struct { type oldDefs struct {

View File

@ -2,34 +2,35 @@ package importer
import ( import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/storage/bolt" "github.com/filebrowser/filebrowser/v2/storage/bolt"
) )
// Import imports an old configuration to a newer database. // Import imports an old configuration to a newer database.
func Import(oldDB, oldConf, newDB string) error { func Import(oldDBPath, oldConf, newDBPath string) error {
old, err := storm.Open(oldDB) oldDB, err := storm.Open(oldDBPath)
if err != nil { if err != nil {
return err return err
} }
defer old.Close() defer oldDB.Close()
new, err := storm.Open(newDB) newDB, err := storm.Open(newDBPath)
if err != nil { if err != nil {
return err return err
} }
defer new.Close() defer newDB.Close()
sto, err := bolt.NewStorage(new) sto, err := bolt.NewStorage(newDB)
if err != nil { if err != nil {
return err return err
} }
err = importUsers(old, sto) err = importUsers(oldDB, sto)
if err != nil { if err != nil {
return err return err
} }
err = importConf(old, oldConf, sto) err = importConf(oldDB, oldConf, sto)
if err != nil { if err != nil {
return err return err
} }

View File

@ -5,10 +5,11 @@ import (
"fmt" "fmt"
"github.com/asdine/storm" "github.com/asdine/storm"
bolt "go.etcd.io/bbolt"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/filebrowser/filebrowser/v2/storage" "github.com/filebrowser/filebrowser/v2/storage"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
bolt "go.etcd.io/bbolt"
) )
type oldUser struct { type oldUser struct {
@ -29,7 +30,7 @@ type oldUser struct {
} }
func readOldUsers(db *storm.DB) ([]*oldUser, error) { func readOldUsers(db *storm.DB) ([]*oldUser, error) {
users := []*oldUser{} var oldUsers []*oldUser
err := db.Bolt.View(func(tx *bolt.Tx) error { err := db.Bolt.View(func(tx *bolt.Tx) error {
return tx.Bucket([]byte("User")).ForEach(func(k []byte, v []byte) error { return tx.Bucket([]byte("User")).ForEach(func(k []byte, v []byte) error {
if len(v) > 0 && string(v)[0] == '{' { if len(v) > 0 && string(v)[0] == '{' {
@ -40,14 +41,14 @@ func readOldUsers(db *storm.DB) ([]*oldUser, error) {
return err return err
} }
users = append(users, user) oldUsers = append(oldUsers, user)
} }
return nil return nil
}) })
}) })
return users, err return oldUsers, err
} }
func convertUsersToNew(old []*oldUser) ([]*users.User, error) { func convertUsersToNew(old []*oldUser) ([]*users.User, error) {

View File

@ -3,6 +3,7 @@ package bolt
import ( import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/asdine/storm/q" "github.com/asdine/storm/q"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/share" "github.com/filebrowser/filebrowser/v2/share"
) )
@ -46,5 +47,9 @@ func (s shareBackend) Save(l *share.Link) error {
} }
func (s shareBackend) Delete(hash string) error { func (s shareBackend) Delete(hash string) error {
return s.db.DeleteStruct(&share.Link{Hash: hash}) err := s.db.DeleteStruct(&share.Link{Hash: hash})
if err == storm.ErrNotFound {
return nil
}
return err
} }

View File

@ -4,6 +4,7 @@ import (
"reflect" "reflect"
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
) )
@ -38,17 +39,17 @@ func (st usersBackend) GetBy(i interface{}) (user *users.User, err error) {
} }
func (st usersBackend) Gets() ([]*users.User, error) { func (st usersBackend) Gets() ([]*users.User, error) {
users := []*users.User{} var allUsers []*users.User
err := st.db.All(&users) err := st.db.All(&allUsers)
if err == storm.ErrNotFound { if err == storm.ErrNotFound {
return nil, errors.ErrNotExist return nil, errors.ErrNotExist
} }
if err != nil { if err != nil {
return users, err return allUsers, err
} }
return users, err return allUsers, err
} }
func (st usersBackend) Update(user *users.User, fields ...string) error { func (st usersBackend) Update(user *users.User, fields ...string) error {

View File

@ -2,6 +2,7 @@ package bolt
import ( import (
"github.com/asdine/storm" "github.com/asdine/storm"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/filebrowser/filebrowser/v2/errors"
) )

View File

@ -7,7 +7,7 @@ import (
"github.com/filebrowser/filebrowser/v2/users" "github.com/filebrowser/filebrowser/v2/users"
) )
// Storage is a storage powered by a Backend whih makes the neccessary // Storage is a storage powered by a Backend which makes the necessary
// verifications when fetching and saving data to ensure consistency. // verifications when fetching and saving data to ensure consistency.
type Storage struct { type Storage struct {
Users *users.Storage Users *users.Storage

View File

@ -40,7 +40,9 @@ func (s *Storage) Get(baseScope string, id interface{}) (user *User, err error)
if err != nil { if err != nil {
return return
} }
user.Clean(baseScope) if err := user.Clean(baseScope); err != nil {
return nil, err
}
return return
} }
@ -52,7 +54,9 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) {
} }
for _, user := range users { for _, user := range users {
user.Clean(baseScope) if err := user.Clean(baseScope); err != nil { //nolint:shadow
return nil, err
}
} }
return users, err return users, err

View File

@ -4,11 +4,11 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"github.com/filebrowser/filebrowser/v2/errors" "github.com/spf13/afero"
"github.com/filebrowser/filebrowser/v2/errors"
"github.com/filebrowser/filebrowser/v2/files" "github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/rules" "github.com/filebrowser/filebrowser/v2/rules"
"github.com/spf13/afero"
) )
// ViewMode describes a view mode. // ViewMode describes a view mode.
@ -52,6 +52,7 @@ var checkableFields = []string{
// Clean cleans up a user and verifies if all its fields // Clean cleans up a user and verifies if all its fields
// are alright to be saved. // are alright to be saved.
//nolint:gocyclo
func (u *User) Clean(baseScope string, fields ...string) error { func (u *User) Clean(baseScope string, fields ...string) error {
if len(fields) == 0 { if len(fields) == 0 {
fields = checkableFields fields = checkableFields