//native
async function connect(auth: RT.F5Bigip): Promise<{
base: string
headers: { [key: string]: string }
release: () => Promise<void>
}> {
const base = auth.base_url.replace(/\/+$/, "")
if (auth.auth_method === "basic") {
return {
base,
headers: {
Authorization: `Basic ${btoa(`${auth.username}:${auth.password}`)}`,
},
release: async () => {},
}
}
const login = await fetch(`${base}/mgmt/shared/authn/login`, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({
username: auth.username,
password: auth.password,
loginProviderName: auth.login_provider_name || "tmos",
}),
})
if (!login.ok) {
throw new Error(`${login.status} ${await login.text()}`)
}
const token: string = (await login.json()).token.token
return {
base,
headers: { "X-F5-Auth-Token": token },
release: async () => {
await fetch(
`${base}/mgmt/shared/authz/tokens/${encodeURIComponent(token)}`,
{
method: "DELETE",
headers: { "X-F5-Auth-Token": token },
}
)
},
}
}
/**
* List active sessions
* List active APM access sessions (session id, logon user, client IP) from /mgmt/tm/apm/access-info. BIG-IP returns every session in one stats document; filtering by user or client IP and the limit/offset window are applied by the script.
*/
export async function main(
auth: RT.F5Bigip,
logon_user: string | undefined,
client_ip: string | undefined,
limit: number | undefined,
offset: number | undefined
) {
const f5 = await connect(auth)
try {
const response = await fetch(`${f5.base}/mgmt/tm/apm/access-info`, {
headers: { ...f5.headers, Accept: "application/json" },
})
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
// entries: { "https://localhost/mgmt/tm/apm/access-info/<session id>": { nestedStats: { entries: { clientIp: { description }, logonUser: { description } } } } }
const stats = await response.json()
const all = Object.entries(stats.entries ?? {}).map(
([link, value]: [string, any]) => {
const fields = value?.nestedStats?.entries ?? {}
return {
session_id: new URL(link).pathname.split("/").pop() as string,
logon_user: fields.logonUser?.description ?? null,
client_ip: fields.clientIp?.description ?? null,
}
}
)
// Older releases list an empty placeholder entry (bug 713389); keep real sessions only.
const sessions = all.filter(
(s) =>
(s.logon_user || s.client_ip) &&
(!logon_user || s.logon_user === logon_user) &&
(!client_ip || s.client_ip === client_ip)
)
const start = offset || 0
const end = start + (limit || 100)
return {
total: sessions.length,
sessions: sessions.slice(start, end),
next_offset: end < sessions.length ? end : null,
}
} finally {
await f5.release()
}
}
Submitted by hugo989 7 hours ago