mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
client/web: pipe unraid csrf token through apiFetch
Ensures that we're sending back the csrf token for all requests made back to unraid clients. Updates tailscale/corp#13775 Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This commit is contained in:
parent
1cd03bc0a1
commit
e952564b59
@ -1,4 +1,5 @@
|
||||
let csrfToken: string
|
||||
let unraidCsrfToken: string | undefined // required for unraid POST requests (#8062)
|
||||
|
||||
// apiFetch wraps the standard JS fetch function with csrf header
|
||||
// management and param additions specific to the web client.
|
||||
@ -8,11 +9,12 @@ let csrfToken: string
|
||||
// (i.e. provide `/data` rather than `api/data`).
|
||||
export function apiFetch(
|
||||
endpoint: string,
|
||||
init?: RequestInit | undefined,
|
||||
addURLParams?: Record<string, string>
|
||||
method: "GET" | "POST",
|
||||
body?: any,
|
||||
params?: Record<string, string>
|
||||
): Promise<Response> {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const nextParams = new URLSearchParams(addURLParams)
|
||||
const nextParams = new URLSearchParams(params)
|
||||
const token = urlParams.get("SynoToken")
|
||||
if (token) {
|
||||
nextParams.set("SynoToken", token)
|
||||
@ -20,9 +22,28 @@ export function apiFetch(
|
||||
const search = nextParams.toString()
|
||||
const url = `api${endpoint}${search ? `?${search}` : ""}`
|
||||
|
||||
var contentType: string
|
||||
if (unraidCsrfToken) {
|
||||
const params = new URLSearchParams()
|
||||
params.append("csrf_token", unraidCsrfToken)
|
||||
if (body) {
|
||||
params.append("ts_data", JSON.stringify(body))
|
||||
}
|
||||
body = params.toString()
|
||||
contentType = "application/x-www-form-urlencoded;charset=UTF-8"
|
||||
} else {
|
||||
body = body ? JSON.stringify(body) : undefined
|
||||
contentType = "application/json"
|
||||
}
|
||||
|
||||
return fetch(url, {
|
||||
...init,
|
||||
headers: withCsrfToken(init?.headers),
|
||||
method: method,
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": contentType,
|
||||
"X-CSRF-Token": csrfToken,
|
||||
},
|
||||
body,
|
||||
}).then((r) => {
|
||||
updateCsrfToken(r)
|
||||
if (!r.ok) {
|
||||
@ -34,13 +55,13 @@ export function apiFetch(
|
||||
})
|
||||
}
|
||||
|
||||
function withCsrfToken(h?: HeadersInit): HeadersInit {
|
||||
return { ...h, "X-CSRF-Token": csrfToken }
|
||||
}
|
||||
|
||||
function updateCsrfToken(r: Response) {
|
||||
const tok = r.headers.get("X-CSRF-Token")
|
||||
if (tok) {
|
||||
csrfToken = tok
|
||||
}
|
||||
}
|
||||
|
||||
export function setUnraidCsrfToken(token?: string) {
|
||||
unraidCsrfToken = token
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ export function Header({
|
||||
|{" "}
|
||||
<button
|
||||
onClick={() =>
|
||||
apiFetch("/local/v0/logout", { method: "POST" })
|
||||
apiFetch("/local/v0/logout", "POST")
|
||||
.then(refreshData)
|
||||
.catch((err) => alert("Logout failed: " + err.message))
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { apiFetch } from "src/api"
|
||||
import { apiFetch, setUnraidCsrfToken } from "src/api"
|
||||
|
||||
export type NodeData = {
|
||||
Profile: UserProfile
|
||||
@ -37,9 +37,12 @@ export default function useNodeData() {
|
||||
|
||||
const refreshData = useCallback(
|
||||
() =>
|
||||
apiFetch("/data")
|
||||
apiFetch("/data", "GET")
|
||||
.then((r) => r.json())
|
||||
.then((d) => setData(d))
|
||||
.then((d: NodeData) => {
|
||||
setData(d)
|
||||
setUnraidCsrfToken(d.IsUnraid ? d.UnraidToken : undefined)
|
||||
})
|
||||
.catch((error) => console.error(error)),
|
||||
[setData]
|
||||
)
|
||||
@ -70,27 +73,7 @@ export default function useNodeData() {
|
||||
: data.AdvertiseExitNode,
|
||||
}
|
||||
|
||||
var body, contentType: string
|
||||
if (data.IsUnraid) {
|
||||
const params = new URLSearchParams()
|
||||
params.append("csrf_token", data.UnraidToken)
|
||||
params.append("ts_data", JSON.stringify(update))
|
||||
body = params.toString()
|
||||
contentType = "application/x-www-form-urlencoded;charset=UTF-8"
|
||||
} else {
|
||||
body = JSON.stringify(update)
|
||||
contentType = "application/json"
|
||||
}
|
||||
|
||||
apiFetch(
|
||||
"/data",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { Accept: "application/json", "Content-Type": contentType },
|
||||
body: body,
|
||||
},
|
||||
{ up: "true" }
|
||||
)
|
||||
apiFetch("/data", "POST", update, { up: "true" })
|
||||
.then((r) => r.json())
|
||||
.then((r) => {
|
||||
setIsPosting(false)
|
||||
|
Loading…
Reference in New Issue
Block a user