mirror of
https://github.com/filebrowser/filebrowser.git
synced 2025-12-29 14:58:26 +00:00
feat: configurable logout page URL for proxy/hook auth (#3884)
Co-authored-by: Henrique Dias <mail@hacdias.com>
This commit is contained in:
parent
701522a060
commit
b9ac45d5da
@ -45,6 +45,7 @@ func addConfigFlags(flags *pflag.FlagSet) {
|
||||
flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type")
|
||||
flags.String("auth.header", "", "HTTP header for auth.method=proxy")
|
||||
flags.String("auth.command", "", "command for auth.method=hook")
|
||||
flags.String("auth.logoutPage", "", "url of custom logout page")
|
||||
|
||||
flags.String("recaptcha.host", "https://www.google.com", "use another host for ReCAPTCHA. recaptcha.net might be useful in China")
|
||||
flags.String("recaptcha.key", "", "ReCaptcha site key")
|
||||
@ -201,6 +202,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
|
||||
fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup)
|
||||
fmt.Fprintf(w, "Hide Login Button:\t%t\n", set.HideLoginButton)
|
||||
fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir)
|
||||
fmt.Fprintf(w, "Logout Page:\t%s\n", set.LogoutPage)
|
||||
fmt.Fprintf(w, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength)
|
||||
fmt.Fprintf(w, "Auth Method:\t%s\n", set.AuthMethod)
|
||||
fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " "))
|
||||
@ -328,6 +330,8 @@ func getSettings(flags *pflag.FlagSet, set *settings.Settings, ser *settings.Ser
|
||||
set.DirMode, err = getAndParseFileMode(flags, flag.Name)
|
||||
case "auth.method":
|
||||
hasAuth = true
|
||||
case "auth.logoutPage":
|
||||
set.LogoutPage, err = flags.GetString(flag.Name)
|
||||
case "branding.name":
|
||||
set.Branding.Name, err = flags.GetString(flag.Name)
|
||||
case "branding.theme":
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
DisableUsedPercentage: false,
|
||||
EnableExec: true,
|
||||
EnableThumbs: true,
|
||||
LogoutPage: "",
|
||||
LoginPage: true,
|
||||
Name: "",
|
||||
NoAuth: false,
|
||||
|
||||
@ -129,6 +129,7 @@ import {
|
||||
disableExternal,
|
||||
disableUsedPercentage,
|
||||
noAuth,
|
||||
logoutPage,
|
||||
loginPage,
|
||||
} from "@/utils/constants";
|
||||
import { files as api } from "@/api";
|
||||
@ -159,7 +160,7 @@ export default {
|
||||
version: () => version,
|
||||
disableExternal: () => disableExternal,
|
||||
disableUsedPercentage: () => disableUsedPercentage,
|
||||
canLogout: () => !noAuth && loginPage,
|
||||
canLogout: () => !noAuth && (loginPage || logoutPage !== "/login"),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useLayoutStore, ["closeHovers", "showHover"]),
|
||||
|
||||
@ -2,7 +2,7 @@ import { useAuthStore } from "@/stores/auth";
|
||||
import router from "@/router";
|
||||
import type { JwtPayload } from "jwt-decode";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { baseURL, noAuth } from "./constants";
|
||||
import { authMethod, baseURL, noAuth, logoutPage } from "./constants";
|
||||
import { StatusError } from "@/api/utils";
|
||||
import { setSafeTimeout } from "@/api/utils";
|
||||
|
||||
@ -18,6 +18,12 @@ export function parseToken(token: string) {
|
||||
authStore.jwt = token;
|
||||
authStore.setUser(data.user);
|
||||
|
||||
// proxy auth with custom logout subject to unknown external timeout
|
||||
if (logoutPage !== "/login" && authMethod === "proxy") {
|
||||
console.warn("idle timeout disabled with proxy auth and custom logout");
|
||||
return;
|
||||
}
|
||||
|
||||
if (authStore.logoutTimer) {
|
||||
clearTimeout(authStore.logoutTimer);
|
||||
}
|
||||
@ -118,6 +124,8 @@ export function logout(reason?: string) {
|
||||
localStorage.setItem("jwt", "");
|
||||
if (noAuth) {
|
||||
window.location.reload();
|
||||
} else if (logoutPage !== "/login") {
|
||||
document.location.href = `${logoutPage}`;
|
||||
} else {
|
||||
if (typeof reason === "string" && reason.trim() !== "") {
|
||||
router.push({
|
||||
|
||||
@ -10,6 +10,7 @@ const version: string = window.FileBrowser.Version;
|
||||
const logoURL = `${staticURL}/img/logo.svg`;
|
||||
const noAuth: boolean = window.FileBrowser.NoAuth;
|
||||
const authMethod = window.FileBrowser.AuthMethod;
|
||||
const logoutPage: string = window.FileBrowser.LogoutPage;
|
||||
const loginPage: boolean = window.FileBrowser.LoginPage;
|
||||
const theme: UserTheme = window.FileBrowser.Theme;
|
||||
const enableThumbs: boolean = window.FileBrowser.EnableThumbs;
|
||||
@ -32,6 +33,7 @@ export {
|
||||
version,
|
||||
noAuth,
|
||||
authMethod,
|
||||
logoutPage,
|
||||
loginPage,
|
||||
theme,
|
||||
enableThumbs,
|
||||
|
||||
28
http/auth.go
28
http/auth.go
@ -12,7 +12,9 @@ import (
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/golang-jwt/jwt/v5/request"
|
||||
|
||||
fbAuth "github.com/filebrowser/filebrowser/v2/auth"
|
||||
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
||||
"github.com/filebrowser/filebrowser/v2/settings"
|
||||
"github.com/filebrowser/filebrowser/v2/users"
|
||||
)
|
||||
|
||||
@ -61,6 +63,22 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
|
||||
return "", request.ErrNoTokenInRequest
|
||||
}
|
||||
|
||||
func renewableErr(err error, d *data) bool {
|
||||
if d.settings.AuthMethod != fbAuth.MethodProxyAuth || err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if d.settings.LogoutPage == settings.DefaultLogoutPage {
|
||||
return false
|
||||
}
|
||||
|
||||
if !errors.Is(err, jwt.ErrTokenExpired) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func withUser(fn handleFunc) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
keyFunc := func(_ *jwt.Token) (interface{}, error) {
|
||||
@ -68,13 +86,9 @@ func withUser(fn handleFunc) handleFunc {
|
||||
}
|
||||
|
||||
var tk authToken
|
||||
token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk))
|
||||
if err != nil || !token.Valid {
|
||||
return http.StatusUnauthorized, nil
|
||||
}
|
||||
|
||||
err = jwt.NewValidator(jwt.WithExpirationRequired()).Validate(tk)
|
||||
if err != nil {
|
||||
p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}), jwt.WithExpirationRequired())
|
||||
token, err := request.ParseFromRequest(r, &extractor{}, keyFunc, request.WithClaims(&tk), request.WithParser(p))
|
||||
if (err != nil || !token.Valid) && !renewableErr(err, d) {
|
||||
return http.StatusUnauthorized, nil
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
||||
"Signup": d.settings.Signup,
|
||||
"NoAuth": d.settings.AuthMethod == auth.MethodNoAuth,
|
||||
"AuthMethod": d.settings.AuthMethod,
|
||||
"LogoutPage": d.settings.LogoutPage,
|
||||
"LoginPage": auther.LoginPage(),
|
||||
"CSS": false,
|
||||
"ReCaptcha": false,
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
const DefaultUsersHomeBasePath = "/users"
|
||||
const DefaultLogoutPage = "/login"
|
||||
const DefaultMinimumPasswordLength = 12
|
||||
const DefaultFileMode = 0640
|
||||
const DefaultDirMode = 0750
|
||||
@ -27,6 +28,7 @@ type Settings struct {
|
||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||
Defaults UserDefaults `json:"defaults"`
|
||||
AuthMethod AuthMethod `json:"authMethod"`
|
||||
LogoutPage string `json:"logoutPage"`
|
||||
Branding Branding `json:"branding"`
|
||||
Tus Tus `json:"tus"`
|
||||
Commands map[string][]string `json:"commands"`
|
||||
|
||||
@ -30,24 +30,34 @@ func (s *Storage) Get() (*Settings, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if set.UserHomeBasePath == "" {
|
||||
set.UserHomeBasePath = DefaultUsersHomeBasePath
|
||||
}
|
||||
|
||||
if set.LogoutPage == "" {
|
||||
set.LogoutPage = DefaultLogoutPage
|
||||
}
|
||||
|
||||
if set.MinimumPasswordLength == 0 {
|
||||
set.MinimumPasswordLength = DefaultMinimumPasswordLength
|
||||
}
|
||||
|
||||
if set.Tus == (Tus{}) {
|
||||
set.Tus = Tus{
|
||||
ChunkSize: DefaultTusChunkSize,
|
||||
RetryCount: DefaultTusRetryCount,
|
||||
}
|
||||
}
|
||||
|
||||
if set.FileMode == 0 {
|
||||
set.FileMode = DefaultFileMode
|
||||
}
|
||||
|
||||
if set.DirMode == 0 {
|
||||
set.DirMode = DefaultDirMode
|
||||
}
|
||||
|
||||
return set, nil
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user