Skip to content

Commit

Permalink
Merge pull request #1270 from modelix/fix/private-keys-from-disk
Browse files Browse the repository at this point in the history
fix(authorization): verification against private keys from disk failed
  • Loading branch information
slisson authored Dec 19, 2024
2 parents e3fb1da + 0ba1639 commit a465888
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.nimbusds.jose.JWSHeader
import com.nimbusds.jose.JWSObject
import com.nimbusds.jose.JWSSigner
import com.nimbusds.jose.JWSVerifier
import com.nimbusds.jose.KeySourceException
import com.nimbusds.jose.crypto.MACSigner
import com.nimbusds.jose.crypto.RSASSASigner
import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory
Expand Down Expand Up @@ -69,7 +70,11 @@ class ModelixJWTUtil {
val keySelectors: List<JWSKeySelector<SecurityContext>> = hmacKeys.map { it.toPair() }.map {
SingleKeyJWSKeySelector<SecurityContext>(it.first, SecretKeySpec(it.second, it.first.name))
} + jwkSources.map {
JWSAlgorithmFamilyJWSKeySelector.fromJWKSource<SecurityContext>(it)
try {
JWSAlgorithmFamilyJWSKeySelector.fromJWKSource<SecurityContext>(it)
} catch (ex: KeySourceException) {
throw KeySourceException("Couldn't retrieve JWKs from $it", ex)
}
}

processor.jwsKeySelector = if (keySelectors.size == 1) keySelectors.single() else CompositeJWSKeySelector(keySelectors)
Expand Down Expand Up @@ -328,6 +333,10 @@ class ModelixJWTUtil {
override fun readFile(): JWKSet {
return JWKSet(ensureValidKey(JWK.parseFromPEMEncodedObjects(file.readText())))
}

override fun toString(): String {
return "PemFileJWKSet[$file]"
}
}

private open inner class FileJWKSet<C : SecurityContext>(val file: File) : JWKSource<C> {
Expand All @@ -340,12 +349,26 @@ class ModelixJWTUtil {

override fun get(jwkSelector: JWKSelector, context: C?): List<JWK?>? {
val jwks = cached.takeIf { System.currentTimeMillis() - loadedAt < fileRefreshTime.inWholeMilliseconds }
?: readFile().also {
?: readFile().let { jwks ->
JWKSet(
jwks.keys.flatMap { key ->
if (key.isPrivate) {
listOf(key, key.toPublicJWK())
} else {
listOf(key)
}
},
)
}.also {
cached = it
loadedAt = System.currentTimeMillis()
}
return jwkSelector.select(jwks)
}

override fun toString(): String {
return "FileJWKSet[$file]"
}
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ fun IAccessControlPersistence.recordKnownRoles(roles: List<String>) {
}

class FileSystemAccessControlPersistence(val file: File) : IAccessControlPersistence {

private val json = Json { ignoreUnknownKeys = true }
private var data: AccessControlData = if (file.exists()) {
Json.decodeFromString<AccessControlData>(file.readText())
json.decodeFromString<AccessControlData>(file.readText())
} else {
AccessControlData()
}.withLegacyRoles()
Expand All @@ -131,7 +131,7 @@ class FileSystemAccessControlPersistence(val file: File) : IAccessControlPersist
}

private fun writeFile() {
file.writeText(Json.encodeToString(data))
file.writeText(json.encodeToString(data))
}
}

Expand Down
43 changes: 43 additions & 0 deletions authorization/src/test/kotlin/org/modelix/authorization/RSATest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,47 @@ class RSATest {
verifyingUtil.verifyToken(tokenString)
}
}

@Test
fun `can verify own tokens`() {
val privateKeyPem1 = """
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAyB+2c/hRX7lhcTKHOom13F7dVnujy1XndcYp4y42NIxRZDui
mOU/inkH6tJsclIftPeYSWnSTWRc5ZG268pRMjD6rMCxCTyo1S7VGuXtdPbfL1ma
kCYfpKALBZdLgrYVkor49CP2cBdKPldYUT7+EpqFxXkaeL073bS3vPPdxN/riuYu
3df3tLe9+st6Tr6+rv1+HK+dRegPok8ryMOogT96QyF7ygLDQ1WW/v/CZI5y+jW1
xEpWnHRkRqHWTtIMjWN6WK+ez1kg4tlQDWmMn4bywmTPRs38weLEMnTUrjfrOxOc
59rWOyE7b186RrDf1F1ezLiVUlLA9L7ThydM3QIDAQABAoIBAEXspsCgrDYpPP3j
bNKsWWn1j5rvOo0KqARDyFEDzZbQzIOcPrTzrR8CKR0IhzHutftyY7iLDBtUjQz9
vA9pMrO532zLK1CR7GAIrBdo7W5n8BXIVjQ1zeqkrRU4Bv9WBfWdL12Gz03dJWjg
9g/1VatEaKdWKES1whw2T9jq0Ls/7/uRTtL31g6SnI/UW5RnZe4TQhNtnTltts6T
eHUU7MjKIlB4VQrHx8up/QdsMIvXihv72jm374nZe6U3e8HmuGb71qXA4YPFju5c
Aict16PVNUTb2ZAylH33NB0k1LlHaCbkQM+Cy3jhhtb1XERXt7tDyS/hiC++HG6b
jlAvqzUCgYEA27OjEbEbw60ca9goC/mafZoDofZWA3aNI+TR15EsFAYQHtoE4DLy
Nrlm0syqqJJwf117jLhu+KpKrJtb36XqfUqnwwISAilnr6OnPT47qs8dbrRIxnap
COh9yw0YerLFPuJ9HTPZMCWs7ufDcXJyuRfjL25lq/kv7jGD6jHRvnMCgYEA6TAG
PK/OyIizT4OtdzNbejQ7W+9wi4tfhjF8OMmgQb6kpsmSmhoaFCQ5SAg9MwqbL2q1
3XSEkPXljONqWmkQZ/2Eo4WHveOKoKj/07LiRucs5jjHyr5pea80z5lTnE8i7MJX
eNSTqi3b9WnV0J0EHhg7qgAbH/q+c5gtiqgkI28CgYB9z0ONSQdmKUaCNzjPirK+
RCjaYW7l8shmCo1jzT0ZhlNK53wtSt9LGSZZhlwfxiPnu4eZkK/zc8jpSNn2m1NJ
RiwFTrUzSbSXbrbBKlcOvCXVlCWsiJzJfiEy2p/u+1paZWZSB7PSj3CVKmDQIUKy
3Yv6SFSugzbARtiMjtTWIwKBgGFKDyAcvap/FkjTiHkWLVFkH2vxD0S5RoaHeOt8
e+dSMgIAUbEHuN+0aU27WkVEZJC49d3KclDEtxw7+bB060pnxIIxAPxhxgHX4Lyj
grLQWrRG9lyJaxpA1kjTEMZDYi/juXkJP/6dmYrfuDyMdh5UP/hiiO6jv/gcgsu5
8THzAoGAUGCnccd4JAXK3/rmkCLT++M18G6ik+qaTMdhGnC9opTDWDvxWF6jHj7w
4/wol7RQf0qmWZr6sSg+dg/cEOvAxBDiayl7WALnEpGhh2+aKkDVIy7JSTOm3fkO
P1Z2sotIDXrYJrdKl/BvWh80ifVYjHp9J/cOhMSyj/HCMhxexhY=
-----END RSA PRIVATE KEY-----
""".trimIndent().trim()

val privateKeyFile = File.createTempFile("modelix_rsa_test", ".pem")
privateKeyFile.deleteOnExit()
privateKeyFile.writeText(privateKeyPem1)

val util = ModelixJWTUtil()
util.loadKeysFromFiles(privateKeyFile)

val tokenString = util.createAccessToken("[email protected]", listOf())
util.verifyToken(tokenString)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.modelix.model.server.store.IGenericStoreClient
import org.modelix.model.server.store.RequiresTransaction

class DBAccessControlPersistence<E>(val store: IGenericStoreClient<E>, val key: E) : IAccessControlPersistence {
private val json = Json { ignoreUnknownKeys }
private val json = Json { ignoreUnknownKeys = true }
override fun read(): AccessControlData {
@OptIn(RequiresTransaction::class)
return store.runReadTransaction {
Expand Down

0 comments on commit a465888

Please sign in to comment.