diff --git a/http/auth.go b/http/auth.go index 4df68262..e1f9f6c5 100644 --- a/http/auth.go +++ b/http/auth.go @@ -177,6 +177,10 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data }) func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) { + tokenExpirationTime := time.Duration(d.settings.TokenExpirationTime) + if tokenExpirationTime == time.Duration(0) { + tokenExpirationTime = 2 * time.Hour + } claims := &authToken{ User: userInfo{ ID: user.ID, @@ -191,7 +195,7 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use }, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()), - ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpirationTime)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(tokenExpirationTime)), Issuer: "File Browser", }, } diff --git a/settings/settings.go b/settings/settings.go index 7df81fc4..cc2b3a75 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -2,7 +2,9 @@ package settings import ( "crypto/rand" + "encoding/json" "strings" + "time" "github.com/filebrowser/filebrowser/v2/rules" ) @@ -14,16 +16,17 @@ type AuthMethod string // Settings contain the main settings of the application. type Settings struct { - Key []byte `json:"key"` - Signup bool `json:"signup"` - CreateUserDir bool `json:"createUserDir"` - UserHomeBasePath string `json:"userHomeBasePath"` - Defaults UserDefaults `json:"defaults"` - AuthMethod AuthMethod `json:"authMethod"` - Branding Branding `json:"branding"` - Commands map[string][]string `json:"commands"` - Shell []string `json:"shell"` - Rules []rules.Rule `json:"rules"` + Key []byte `json:"key"` + Signup bool `json:"signup"` + CreateUserDir bool `json:"createUserDir"` + UserHomeBasePath string `json:"userHomeBasePath"` + Defaults UserDefaults `json:"defaults"` + AuthMethod AuthMethod `json:"authMethod"` + Branding Branding `json:"branding"` + Commands map[string][]string `json:"commands"` + Shell []string `json:"shell"` + Rules []rules.Rule `json:"rules"` + TokenExpirationTime Duration `json:"tokenExpirationTime"` // 0 is treated as 2 Hours } // GetRules implements rules.Provider. @@ -64,3 +67,49 @@ func GenerateKey() ([]byte, error) { return b, nil } + +type Duration time.Duration // support json Marshal/Unmarshal for time.Duration + +func (dur Duration) MarshalJSON() ([]byte, error) { + return []byte("\"" + time.Duration(dur).String() + "\""), nil +} + +func (dur *Duration) UnmarshalJSON(data []byte) error { + var dStr string + err := json.Unmarshal(data, &dStr) + if err != nil { + return err + } + if dStr == "" { + *dur = 0 // zero value + return nil + } + d, err := time.ParseDuration(dStr) + if err != nil { + return err + } + *dur = Duration(d) + return nil +} + +func (dur Duration) MarshalYAML() (interface{}, error) { + return time.Duration(dur).String(), nil +} + +func (dur *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { + var dStr string + err := unmarshal(&dStr) + if err != nil { + return err + } + if dStr == "" { + *dur = 0 // zero value + return nil + } + d, err := time.ParseDuration(dStr) + if err != nil { + return err + } + *dur = Duration(d) + return nil +} diff --git a/settings/settings_test.go b/settings/settings_test.go new file mode 100644 index 00000000..37c84ea3 --- /dev/null +++ b/settings/settings_test.go @@ -0,0 +1,48 @@ +package settings + +import ( + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func TestDuration(t *testing.T) { + testCases := []struct { + str string + value Duration + }{ + {`"1s"`, Duration(time.Second)}, + {`"1m"`, Duration(time.Minute)}, + {`"1h"`, Duration(time.Hour)}, + {`null`, 0}, + {`""`, 0}, + } + codecs := []struct { + name string + marshal func(interface{}) ([]byte, error) + unmarshal func([]byte, interface{}) error + }{ + {"json", json.Marshal, json.Unmarshal}, + {"yaml", yaml.Marshal, yaml.Unmarshal}, + } + for _, tc := range testCases { + for _, codec := range codecs { + t.Run(codec.name, func(t *testing.T) { + // str --> dur --> mid_str(may different from str) --> dur + var dur Duration + err := codec.unmarshal([]byte(tc.str), &dur) + require.NoError(t, err) + require.Equal(t, tc.value, dur) + + midStr, err := codec.marshal(dur) + require.NoError(t, err) + err = codec.unmarshal(midStr, &dur) + require.NoError(t, err) + require.Equal(t, tc.value, dur) + }) + } + } +}