Android: send X-Kvitt-App app key on every server request (D-126)
The reverse proxy closes requests from outside the home LAN that lack X-Kvitt-App: <key>. The key is the build property kvitt.appKey (-Pkvitt.appKey, else android/local.properties, else empty = no header), validated as 16-128 printable ASCII without whitespace, exposed as BuildConfig.APP_KEY and passed via AppBuildInfo.appKey to KvittApi (every call, /health and other unauthenticated calls included) and the Coil picture client (PictureAuth). AppBuildInfo.toString hides the key. Tests use a made-up key; README documents the property. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
@@ -32,6 +32,19 @@ require(Regex("[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+"
|
||||
"kvitt.publicHost must be a host name such as kvitt.duckdns.org (no scheme, port or path), was '$publicHost'"
|
||||
}
|
||||
|
||||
// D-126: the app key for the reverse proxy (SPEC §8.5, §14.3.1). Every request to the server carries X-Kvitt-App: <key>;
|
||||
// outside the home LAN the proxy closes any request without it. From -Pkvitt.appKey, else the key kvitt.appKey in
|
||||
// android/local.properties (the owner sets it there; never committed), else empty: no header (debug builds against the
|
||||
// dev server need none). A secret: never logged, and the failure below names its length only, never its value.
|
||||
val appKey: String =
|
||||
providers.gradleProperty("kvitt.appKey").orNull?.trim()?.takeIf { it.isNotEmpty() }
|
||||
?: localProperties.getProperty("kvitt.appKey")?.trim()?.takeIf { it.isNotEmpty() }
|
||||
?: ""
|
||||
require(appKey.isEmpty() || Regex("[!-~]{16,128}").matches(appKey)) {
|
||||
"kvitt.appKey must be 16 to 128 printable ASCII characters without whitespace (D-126); the value given has " +
|
||||
"${appKey.length} characters"
|
||||
}
|
||||
|
||||
// SPEC §10.5.3 (D-082): the pinned NPM root (D-120) replaces the system anchors for kvitt.publicHost only when this is true.
|
||||
val pinnedRoot = providers.gradleProperty("kvitt.tls.pinnedRoot").orElse("false").get().toBoolean()
|
||||
|
||||
@@ -65,6 +78,7 @@ android {
|
||||
versionCode = providers.gradleProperty("kvitt.versionCode").orElse("1").get().toInt()
|
||||
versionName = providers.gradleProperty("kvitt.versionName").orElse("dev").get()
|
||||
buildConfigField("String", "PUBLIC_HOST", "\"$publicHost\"")
|
||||
buildConfigField("String", "APP_KEY", "\"${appKey.replace("\\", "\\\\").replace("\"", "\\\"")}\"")
|
||||
manifestPlaceholders["publicHost"] = publicHost
|
||||
}
|
||||
// SPEC G-8 and AC-19.1: the app is English (fallback) and Swedish only; library translations of other languages
|
||||
@@ -72,7 +86,8 @@ android {
|
||||
androidResources {
|
||||
localeFilters += listOf("en", "sv")
|
||||
}
|
||||
// BuildConfig carries versionCode, versionName and DEBUG into AppBuildInfo (:data, D-041), and PUBLIC_HOST (D-121).
|
||||
// BuildConfig carries versionCode, versionName, DEBUG and APP_KEY (D-126) into AppBuildInfo (:data, D-041), and
|
||||
// PUBLIC_HOST (D-121).
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user