@samitouri / QOSami-HFS / commits / ac9f3a50

removed allow_session_ip_change from config, as it's useless now that we have the same in the frontend

Massimo Melina committed Mar 9, 2025 at 12:31 UTC ac9f3a50db01a3fa3dcd6d095c21e9a646907401
7 files changed +15 -18
admin/src/LoginRequired.ts
+4 -4
@@ -2,7 +2,7 @@
2
3 import { state, useSnapState } from './state'
4 import { createElement as h, Fragment, useEffect, useRef, useState } from 'react'
5 -import { CFG, HTTP_FORBIDDEN, HTTP_UNAUTHORIZED, makeSessionRefresher } from './misc'
5 +import { ALLOW_SESSION_IP_CHANGE, HTTP_FORBIDDEN, HTTP_UNAUTHORIZED, makeSessionRefresher } from './misc'
6 import { BoolField, Form } from '@hfs/mui-grid-form'
7 import { apiCall } from './api'
8 import { srpClientSequence } from '@hfs/shared'
@@ -22,7 +22,7 @@ export function LoginRequired({ children }: any) {
22 }
23
24 function LoginForm() {
25 - const [values, setValues] = useState({ username: '', password: '', ipChange: false })
25 + const [values, setValues] = useState({ username: '', password: '', [ALLOW_SESSION_IP_CHANGE]: false })
26 const [error, setError] = useState('')
27 const formRef = useRef<HTMLFormElement>()
28 const empty = formRef.current?.querySelector('input[value=""]')
@@ -39,7 +39,7 @@ function LoginForm() {
39 fields: [
40 { k: 'username', autoComplete: 'username', autoFocus: true, required: true },
41 { k: 'password', type: 'password', autoComplete: 'current-password', required: true },
42 - { k: 'ipChange', comp: BoolField, label: "Allow IP change during this session" },
42 + { k: ALLOW_SESSION_IP_CHANGE, comp: BoolField, label: "Allow IP change during this session" },
43 ],
44 addToBar: [ error && h(Alert, { severity: 'error', sx: { flex: 1 } }, error) ],
45 saveOnEnter: true,
@@ -50,7 +50,7 @@ function LoginForm() {
50 try {
51 setError('')
52 await login(values.username, values.password, {
53 - [CFG.allow_session_ip_change]: values.ipChange
53 + [ALLOW_SESSION_IP_CHANGE]: values[ALLOW_SESSION_IP_CHANGE]
54 })
55 }
56 catch(e) {
config.md
-1
@@ -121,7 +121,6 @@ Configuration can be done in several ways
121 Multiple URLs are supported, and you can specify one for each line.
122 - `outbound_proxy` if you need outgoing http(s) requests to pass through an HTTP proxy. Default is none.
123 - `auto_basic` automatically detect (based on user-agent) when the basic web interface should be served, to support legacy browsers. Default is true. No UI.
124 -- `allow_session_ip_change` should requests of the same login session be allowed from different IP addresses. Default is false, to prevent cookie stealing. You can set it `true` to always allow it, or `https` to allow only on https, where stealing the cookie is harder. No UI.
124 - `authorization_header` support Authentication HTTP header. Default is true. No UI.
125 - `cache_control_disk_files` number of seconds after which the browser should bypass the cache and check the server for an updated version of the file. Default is 5. No UI.
126 - `disable_custom_html` disable the content of `custom_html`. Default is false.
frontend/src/login.ts
+4 -4
@@ -5,7 +5,7 @@ import { state, useSnapState } from './state'
5 import { alertDialog, newDialog, toast } from './dialog'
6 import {
7 getHFS, hIcon, makeSessionRefresher, srpClientSequence, working, fallbackToBasicAuth,
8 - HTTP_CONFLICT, HTTP_UNAUTHORIZED, CFG, HTTP_METHOD_NOT_ALLOWED,
8 + HTTP_CONFLICT, HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED, ALLOW_SESSION_IP_CHANGE,
9 } from './misc'
10 import { createElement as h, Fragment, useEffect, useRef } from 'react'
11 import { reloadList } from './useFetchList'
@@ -107,8 +107,8 @@ export async function loginDialog(closable=true, reloadAfter=true) {
107 h('div', { className: 'submit' },
108 h('button', { type: 'submit' }, t`Continue`)),
109 h('div', { id: 'login-options' },
110 - h(Checkbox, { ref: ipRef, id: 'allow_session_ip_change' },
111 - t('allow_session_ip_change', "Allow IP change during this session")),
110 + h(Checkbox, { ref: ipRef, id: ALLOW_SESSION_IP_CHANGE },
111 + t(ALLOW_SESSION_IP_CHANGE, "Allow IP change during this session")),
112 ),
113 )
114
@@ -131,7 +131,7 @@ export async function loginDialog(closable=true, reloadAfter=true) {
131 going = true
132 try {
133 const res = await login(u, password, {
134 - [CFG.allow_session_ip_change]: ipRef.current?.checked,
134 + [ALLOW_SESSION_IP_CHANGE]: ipRef.current?.checked,
135 ...rest
136 })
137 await close(true)
src/auth.ts
+3 -3
@@ -1,9 +1,9 @@
1 import { Account, getAccount, normalizeUsername, updateAccount } from './perm'
2 -import { HTTP_NOT_ACCEPTABLE, HTTP_SERVER_ERROR } from './cross-const'
2 +import { ALLOW_SESSION_IP_CHANGE, HTTP_NOT_ACCEPTABLE, HTTP_SERVER_ERROR } from './cross-const'
3 import { SRPParameters, SRPRoutines, SRPServerSession } from 'tssrp6a'
4 import { Context } from 'koa'
5 import { srpClientPart } from './srp'
6 -import { CFG, DAY } from './cross'
6 +import { DAY } from './cross'
7 import { expiringCache } from './expiringCache'
8 import { createHash } from 'node:crypto'
9 import events from './events'
@@ -67,7 +67,7 @@ export async function setLoggedIn(ctx: Context, username: string | false) {
67 await events.emitAsync('finalizingLogin', { ctx, username, inputs: { ...ctx.state.params, ...ctx.query } })
68 s.username = normalizeUsername(username)
69 s.ts = Date.now()
70 - const k = CFG.allow_session_ip_change
70 + const k = ALLOW_SESSION_IP_CHANGE
71 s[k] = k in ctx.query || Boolean(ctx.state.params?.[k]) || undefined // login APIs will get ctx.state.params, others can rely on ctx.query
72 if (!a.expire && a.days_to_live)
73 updateAccount(a, { expire: new Date(Date.now() + a.days_to_live! * DAY) })
src/cross-const.ts
+1
@@ -12,6 +12,7 @@ export const UPLOAD_RESUMABLE = 'upload.resumable'
12 export const UPLOAD_RESUMABLE_HASH = 'upload.hash'
13 export const UPLOAD_REQUEST_STATUS = 'upload.status'
14 export const PREVIOUS_TAG = 'previous'
15 +export const ALLOW_SESSION_IP_CHANGE = 'allow_session_ip_change'
16
17 export const HTTP_OK = 200
18 export const HTTP_NO_CONTENT = 204
src/cross.ts
+1 -2
@@ -31,8 +31,7 @@ export const THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' }
31 export const CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown', 'dynamic_dns_url',
32 'log', 'error_log', 'log_rotation', 'dont_log_net', 'log_gui', 'log_api', 'log_ua', 'log_spam', 'track_ips',
33 'max_downloads', 'max_downloads_per_ip', 'max_downloads_per_account', 'roots', 'force_address', 'split_uploads',
34 - 'allow_session_ip_change', 'force_lang', 'suspend_plugins', 'base_url', 'size_1024', 'disable_custom_html',
35 - 'comments_storage'])
34 + 'force_lang', 'suspend_plugins', 'base_url', 'size_1024', 'disable_custom_html', 'comments_storage'])
35 export const LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' }
36 export type Dict<T=any> = Record<string, T>
37 export type Falsy = false | null | undefined | '' | 0
src/middlewares.ts
+2 -4
@@ -3,7 +3,7 @@
3 import compress from 'koa-compress'
4 import Koa from 'koa'
5 import { API_URI, DEV, HTTP_FOOL } from './const'
6 -import { CFG, DAY, dirTraversal, isLocalHost, netMatches, splitAt, stream2string, tryJson } from './misc'
6 +import { ALLOW_SESSION_IP_CHANGE, DAY, dirTraversal, isLocalHost, netMatches, splitAt, stream2string, tryJson } from './misc'
7 import { Readable } from 'stream'
8 import { applyBlock } from './block'
9 import { Account, accountCanLogin, getAccount, getFromAccount } from './perm'
@@ -16,7 +16,6 @@ import session from 'koa-session'
16 import { app } from './index'
17 import events from './events'
18
19 -const allowSessionIpChange = defineConfig<boolean | 'https'>(CFG.allow_session_ip_change, false)
19 const forceHttps = defineConfig('force_https', true)
20 const ignoreProxies = defineConfig('ignore_proxies', false)
21 const allowAuthorizationHeader = defineConfig('authorization_header', true)
@@ -52,8 +51,7 @@ export let cloudflareDetected: undefined | Date
51 export const someSecurity: Koa.Middleware = (ctx, next) => {
52 ctx.request.ip = normalizeIp(ctx.ip)
53 const ss = ctx.session
55 - const allowIpChange = ss?.[allowSessionIpChange.key()] ?? allowSessionIpChange.get() // session can override server setting
56 - if (ss?.username && (!allowIpChange || !ctx.secure && allowIpChange === 'https'))
54 + if (ss?.username && !ss?.[ALLOW_SESSION_IP_CHANGE])
55 if (!ss.ip)
56 ss.ip = ctx.ip
57 else if (ss.ip !== ctx.ip) {