Harden admin deployment and local acceptance
Enforce mTLS and least-privilege runtime boundaries while adding repeatable MySQL 8.4 and Docker smoke checks that require no production secrets.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package com.osglab.account.config
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldContainExactly
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.string.shouldNotContain
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
class SmokeDeploymentTest : FunSpec({
|
||||
val root = Path.of(System.getProperty("user.dir"))
|
||||
|
||||
test("smoke Compose builds locally and publishes only loopback ports") {
|
||||
val compose = root.read("compose.smoke.yaml")
|
||||
|
||||
compose shouldContain "image: mysql:8.4"
|
||||
compose shouldContain "build:"
|
||||
compose shouldContain "dockerfile: Dockerfile"
|
||||
compose shouldContain "127.0.0.1:\${SMOKE_APP_PORT"
|
||||
compose shouldContain "internal: true"
|
||||
compose shouldContain "network_mode: none"
|
||||
compose shouldContain "read_only: true"
|
||||
compose shouldContain "no-new-privileges:true"
|
||||
compose shouldNotContain "SMOKE_MYSQL_PORT"
|
||||
compose shouldNotContain "0.0.0.0:"
|
||||
compose shouldNotContain "ghcr.io/"
|
||||
}
|
||||
|
||||
test("smoke runner isolates secrets providers and cleanup") {
|
||||
val runner = root.read("deploy/smoke-local.sh")
|
||||
|
||||
runner shouldContain "set -euo pipefail"
|
||||
runner shouldContain "mktemp -d"
|
||||
runner shouldContain "trap cleanup EXIT INT TERM"
|
||||
runner shouldContain "down --volumes --remove-orphans --rmi local"
|
||||
runner shouldContain "APPLE_JWKS_URL=http://127.0.0.1:9/"
|
||||
runner shouldContain "VOLCENGINE_ASR_ENDPOINT=ws://127.0.0.1:9/"
|
||||
runner shouldContain "DEEPSEEK_ENDPOINT=http://127.0.0.1:9/"
|
||||
runner shouldContain "Flyway history was not exactly successful V1-V8"
|
||||
runner shouldContain "first ledger page omitted nextCursor"
|
||||
runner shouldContain "DELETE FROM admin_sessions WHERE expires_at < UTC_TIMESTAMP()"
|
||||
runner shouldNotContain "appleid.apple.com"
|
||||
runner shouldNotContain "api.deepseek.com"
|
||||
runner shouldNotContain "openspeech.bytedance.com"
|
||||
}
|
||||
|
||||
test("runtime grants cover every migrated table without mutable history privileges") {
|
||||
val grants = root.read("deploy/smoke/runtime-grants.sql")
|
||||
val migrationTables = (1..8)
|
||||
.flatMap { version ->
|
||||
val migration = Files.list(root.resolve("src/main/resources/db/migration")).use { paths ->
|
||||
paths.filter { it.fileName.toString().startsWith("V${version}__") }
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
}
|
||||
CREATE_TABLE.findAll(Files.readString(migration))
|
||||
.map { it.groupValues[1] }
|
||||
.toList()
|
||||
}
|
||||
.toSet()
|
||||
val grantedTables = GRANTED_TABLE.findAll(grants)
|
||||
.map { it.groupValues[1] }
|
||||
.toSet()
|
||||
|
||||
grantedTables.sorted() shouldContainExactly migrationTables.sorted()
|
||||
grants shouldContain "GRANT INSERT, UPDATE, DELETE ON osg_account_smoke.admin_sessions"
|
||||
grants shouldNotContain "UPDATE ON osg_account_smoke.credit_ledger"
|
||||
grants shouldNotContain "DELETE ON osg_account_smoke.credit_ledger"
|
||||
grants shouldNotContain "UPDATE ON osg_account_smoke.admin_audit_log"
|
||||
grants shouldNotContain "DELETE ON osg_account_smoke.admin_audit_log"
|
||||
grants shouldNotContain "UPDATE ON osg_account_smoke.admin_credit_grants"
|
||||
grants shouldNotContain "DELETE ON osg_account_smoke.admin_credit_grants"
|
||||
}
|
||||
|
||||
test("fixture forces the ledger cursor boundary") {
|
||||
val fixture = root.read("deploy/smoke/fixture.sql")
|
||||
|
||||
fixture shouldContain "WHERE value < 101"
|
||||
fixture shouldContain "INSERT INTO credit_ledger"
|
||||
fixture shouldNotContain "osg_account"
|
||||
}
|
||||
})
|
||||
|
||||
private fun Path.read(relativePath: String): String =
|
||||
Files.readString(resolve(relativePath))
|
||||
|
||||
private val CREATE_TABLE = Regex("""CREATE TABLE\s+([a-z0-9_]+)""", RegexOption.IGNORE_CASE)
|
||||
private val GRANTED_TABLE = Regex("""ON osg_account_smoke\.([a-z0-9_]+)""")
|
||||
Reference in New Issue
Block a user