54 lines
1.8 KiB
Kotlin
54 lines
1.8 KiB
Kotlin
package de.hoennig.gittally.server
|
|
|
|
import java.nio.file.Files
|
|
import java.nio.file.Path
|
|
import java.nio.file.attribute.PosixFilePermissions
|
|
import java.security.MessageDigest
|
|
import java.security.SecureRandom
|
|
|
|
/**
|
|
* Guards the mutating build endpoints with a shared secret, like the legacy cancel
|
|
* token. The token is generated once and persisted (mode 600) so operators — and
|
|
* the step-08 UI, server-side — can read it; delete the file to rotate it.
|
|
*/
|
|
class ControlTokenService(
|
|
private val tokenFile: Path,
|
|
) {
|
|
@Synchronized
|
|
fun token(): String {
|
|
if (Files.isRegularFile(tokenFile)) {
|
|
Files
|
|
.readAllLines(tokenFile)
|
|
.firstOrNull()
|
|
?.trim()
|
|
?.takeIf { it.isNotEmpty() }
|
|
?.let { return it }
|
|
}
|
|
val token = generateToken()
|
|
Files.createDirectories(tokenFile.parent)
|
|
Files.writeString(tokenFile, token + "\n")
|
|
restrictToOwner(tokenFile)
|
|
return token
|
|
}
|
|
|
|
/** Constant-time comparison; null or blank never matches. */
|
|
fun matches(submittedToken: String?): Boolean =
|
|
!submittedToken.isNullOrBlank() &&
|
|
MessageDigest.isEqual(submittedToken.toByteArray(), token().toByteArray())
|
|
|
|
/** 24 random bytes as hex, like legacy `openssl rand -hex 24`. */
|
|
private fun generateToken(): String {
|
|
val bytes = ByteArray(24)
|
|
SecureRandom().nextBytes(bytes)
|
|
return bytes.joinToString("") { "%02x".format(it) }
|
|
}
|
|
|
|
private fun restrictToOwner(file: Path) {
|
|
try {
|
|
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rw-------"))
|
|
} catch (_: UnsupportedOperationException) {
|
|
// non-POSIX filesystem; the file stays with default permissions
|
|
}
|
|
}
|
|
}
|