mirror of
https://github.com/rustfs/console.git
synced 2026-08-28 19:47:21 +08:00
fix:Redirect loop in console rustfs/rustfs#1941
This commit is contained in:
@@ -19,7 +19,7 @@ export function DashboardAuthGuard({ children }: { children: React.ReactNode })
|
||||
|
||||
useEffect(() => {
|
||||
if (isReady && !isAuthenticated) {
|
||||
router.replace("/auth/login?unauthorized=true")
|
||||
router.replace("/auth/login/?unauthorized=true")
|
||||
}
|
||||
}, [isAuthenticated, isReady, router])
|
||||
|
||||
@@ -34,7 +34,7 @@ export function DashboardAuthGuard({ children }: { children: React.ReactNode })
|
||||
if (!isReady || !isAuthenticated || isAdmin) return
|
||||
if (isLoading || !hasFetchedPolicy) return
|
||||
if (!canAccessPath(pathname)) {
|
||||
router.replace("/403")
|
||||
router.replace("/403/")
|
||||
}
|
||||
}, [isReady, isAuthenticated, isAdmin, isLoading, userPolicy, hasFetchedPolicy, canAccessPath, pathname, router])
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from "react"
|
||||
import { usePathname, useRouter } from "next/navigation"
|
||||
import { ApiClient } from "@/lib/api-client"
|
||||
import { AwsClient } from "@/lib/aws4fetch"
|
||||
import { ApiErrorHandler } from "@/lib/api-error-handler"
|
||||
@@ -19,6 +20,8 @@ export function ApiProvider({ children }: { children: React.ReactNode }) {
|
||||
const { credentials, isAuthenticated, logout } = useAuth()
|
||||
const [apiClient, setApiClient] = useState<ApiClient | null>(null)
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || !credentials?.AccessKeyId) {
|
||||
@@ -51,10 +54,19 @@ export function ApiProvider({ children }: { children: React.ReactNode }) {
|
||||
const errorHandler = new ApiErrorHandler({
|
||||
onUnauthorized: async () => {
|
||||
logout()
|
||||
window.location.href = getLoginRoute()
|
||||
router.replace("/auth/login/")
|
||||
},
|
||||
onForbidden: async () => {
|
||||
window.location.href = buildRoute("/403")
|
||||
onForbidden: async (url) => {
|
||||
// If the forbidden error happens on the accountinfo endpoint, it means the session is invalid
|
||||
// or the user has no permissions at all. In this case, we should log out.
|
||||
if (url?.includes("/is-admin") || url?.includes("/accountinfo") || url?.includes("/version")) {
|
||||
logout()
|
||||
router.replace("/auth/login/")
|
||||
return
|
||||
}
|
||||
|
||||
if (pathname === "/403/") return
|
||||
router.replace("/403/")
|
||||
},
|
||||
})
|
||||
|
||||
@@ -71,7 +83,7 @@ export function ApiProvider({ children }: { children: React.ReactNode }) {
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [isAuthenticated, credentials?.AccessKeyId, credentials?.SecretAccessKey, credentials?.SessionToken, logout])
|
||||
}, [isAuthenticated, credentials?.AccessKeyId, credentials?.SecretAccessKey, credentials?.SessionToken, logout, pathname, router])
|
||||
|
||||
return <ApiContext.Provider value={{ api: apiClient, isReady }}>{children}</ApiContext.Provider>
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ interface AuthContextValue {
|
||||
|
||||
const AuthContext = createContext<AuthContextValue | null>(null)
|
||||
|
||||
function isValidCredentials(credentials: Credentials): boolean {
|
||||
function isValidCredentials(credentials: Credentials | undefined): boolean {
|
||||
if (
|
||||
!credentials?.AccessKeyId ||
|
||||
!credentials?.SecretAccessKey ||
|
||||
@@ -46,8 +46,8 @@ function isValidCredentials(credentials: Credentials): boolean {
|
||||
}
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [store, setStore] = useLocalStorage<Credentials>("auth.credentials", {})
|
||||
const [isAdminStore, setIsAdminStore] = useLocalStorage<boolean>("auth.isAdmin", false)
|
||||
const [store, setStore] = useLocalStorage<Credentials | undefined>("auth.credentials", undefined)
|
||||
const [isAdminStore, setIsAdminStore] = useLocalStorage<boolean | undefined>("auth.isAdmin", undefined)
|
||||
const [permanentStore, setPermanentStore] = useLocalStorage<Credentials | undefined>("auth.permanent", undefined)
|
||||
|
||||
const setCredentials = useCallback(
|
||||
@@ -77,7 +77,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
)
|
||||
|
||||
const getIsAdmin = useCallback(() => {
|
||||
return isAdminStore
|
||||
return !!isAdminStore
|
||||
}, [isAdminStore])
|
||||
|
||||
const login = useCallback(
|
||||
@@ -125,9 +125,9 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
)
|
||||
|
||||
const logout = useCallback(() => {
|
||||
setStore({})
|
||||
setStore(undefined)
|
||||
setPermanentStore(undefined)
|
||||
setIsAdminStore(false)
|
||||
setIsAdminStore(undefined)
|
||||
}, [setStore, setPermanentStore, setIsAdminStore])
|
||||
|
||||
const logoutAndRedirect = useCallback(() => {
|
||||
@@ -149,7 +149,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
credentials,
|
||||
permanentCredentials: permanentStore,
|
||||
isAuthenticated,
|
||||
isAdmin: isAdminStore,
|
||||
isAdmin: !!isAdminStore,
|
||||
}),
|
||||
[
|
||||
login,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from "react"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { S3Client } from "@aws-sdk/client-s3"
|
||||
import { useAuth } from "@/contexts/auth-context"
|
||||
import { configManager } from "@/lib/config"
|
||||
@@ -40,6 +41,7 @@ export function S3Provider({ children }: { children: React.ReactNode }) {
|
||||
const { credentials, isAuthenticated, logout } = useAuth()
|
||||
const [s3Client, setS3Client] = useState<S3Client | null>(null)
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || !credentials?.AccessKeyId) {
|
||||
@@ -123,7 +125,7 @@ export function S3Provider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
if (err?.$metadata?.httpStatusCode === 401) {
|
||||
logout()
|
||||
window.location.href = getLoginRoute()
|
||||
router.replace("/auth/login/")
|
||||
return { response: { statusCode: 401, headers: {} } }
|
||||
}
|
||||
if (err?.$metadata?.httpStatusCode === 403) {
|
||||
@@ -132,7 +134,7 @@ export function S3Provider({ children }: { children: React.ReactNode }) {
|
||||
const isInvalidAccessKey = codeText === "invalidaccesskeyid"
|
||||
if (isUnauthorizedAccess || isInvalidAccessKey) {
|
||||
logout()
|
||||
window.location.href = getLoginRoute()
|
||||
router.replace("/auth/login/")
|
||||
return { response: { statusCode: 401, headers: {} } }
|
||||
}
|
||||
}
|
||||
@@ -153,7 +155,7 @@ export function S3Provider({ children }: { children: React.ReactNode }) {
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [isAuthenticated, credentials?.AccessKeyId, credentials?.SecretAccessKey, credentials?.SessionToken, logout])
|
||||
}, [isAuthenticated, credentials?.AccessKeyId, credentials?.SecretAccessKey, credentials?.SessionToken, logout, router])
|
||||
|
||||
return <S3Context.Provider value={{ client: s3Client, isReady }}>{children}</S3Context.Provider>
|
||||
}
|
||||
|
||||
+4
-4
@@ -73,7 +73,7 @@ export class ApiClient {
|
||||
|
||||
if (response.status === 401) {
|
||||
if (this.errorHandler) {
|
||||
await this.errorHandler.handle401()
|
||||
await this.errorHandler.handle401(url)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -102,14 +102,14 @@ export class ApiClient {
|
||||
|
||||
if (this.errorHandler) {
|
||||
if (isUnauthorizedAccess || isInvalidAccessKey) {
|
||||
await this.errorHandler.handle401()
|
||||
await this.errorHandler.handle401(url)
|
||||
} else {
|
||||
await this.errorHandler.handle403()
|
||||
await this.errorHandler.handle403(url)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if (this.errorHandler) {
|
||||
await this.errorHandler.handle403()
|
||||
await this.errorHandler.handle403(url)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
+10
-10
@@ -15,37 +15,37 @@ export class ApiErrorHandler implements IApiErrorHandler {
|
||||
this.onServerError = callbacks?.onServerError
|
||||
}
|
||||
|
||||
async handle401(): Promise<void> {
|
||||
async handle401(url?: string): Promise<void> {
|
||||
if (this.onUnauthorized) {
|
||||
await this.onUnauthorized()
|
||||
await this.onUnauthorized(url)
|
||||
}
|
||||
}
|
||||
|
||||
async handle403(): Promise<void> {
|
||||
async handle403(url?: string): Promise<void> {
|
||||
if (this.onForbidden) {
|
||||
await this.onForbidden()
|
||||
await this.onForbidden(url)
|
||||
}
|
||||
}
|
||||
|
||||
async handleServerError(): Promise<void> {
|
||||
async handleServerError(url?: string): Promise<void> {
|
||||
if (this.onServerError) {
|
||||
await this.onServerError()
|
||||
await this.onServerError(url)
|
||||
}
|
||||
}
|
||||
|
||||
async handleByStatus(status: number): Promise<void> {
|
||||
async handleByStatus(status: number, url?: string): Promise<void> {
|
||||
switch (status) {
|
||||
case 401:
|
||||
await this.handle401()
|
||||
await this.handle401(url)
|
||||
break
|
||||
case 403:
|
||||
await this.handle403()
|
||||
await this.handle403(url)
|
||||
break
|
||||
case 500:
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
await this.handleServerError()
|
||||
await this.handleServerError(url)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+5
-5
@@ -1,8 +1,8 @@
|
||||
export type ApiErrorHandlerCallback = () => void | Promise<void>
|
||||
export type ApiErrorHandlerCallback = (url?: string) => void | Promise<void>
|
||||
|
||||
export interface IApiErrorHandler {
|
||||
handle401(): Promise<void>
|
||||
handle403(): Promise<void>
|
||||
handleServerError(): Promise<void>
|
||||
handleByStatus(status: number): Promise<void>
|
||||
handle401(url?: string): Promise<void>
|
||||
handle403(url?: string): Promise<void>
|
||||
handleServerError(url?: string): Promise<void>
|
||||
handleByStatus(status: number, url?: string): Promise<void>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user