Skip to content

Commit

Permalink
Merge branch 'main' into clean-up-entry-with-ttl-job
Browse files Browse the repository at this point in the history
  • Loading branch information
mrFlick72 committed Nov 20, 2024
2 parents 651a7c7 + 7755854 commit 733e527
Show file tree
Hide file tree
Showing 22 changed files with 491 additions and 33 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ Right now it is based, as said before to the latest version on spring oauth2/ope

### local environment

For more details please follow to this link [readme.md](local-environment%2Freadme.md)
For more details please follow to this link [readme.md](local-environment%2Freadme.md)

### profiling

The application configuration is very versatile and you can decide what persistence and key management provider to use AWS or not AWS native.
For more details please refer to the detailed page [here](docs/profiles.md)
28 changes: 28 additions & 0 deletions docs/profiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Profile

VAuthenticator can be configured to be strongly AWS integrated using DynamoDB for the persistence layer and KMS for Key management.

If your organization or for you run VAuthenticator so tiny integrated with AWS does not is suitable you can decide to switch postgresql instead dynamodb for the persistence
and plain java security key management instead of KMS

All what you need is enable the relative spring profile as below:

use ```spring.profiles.active``` with

- ```database```: to use PostgresSQL
- ```dynamo```: to use DyanamoDB
- ```kms``` to use KMS
- omitting ```kms``` to use plain java security api

in case of plain java security implementation the follow configuration is required:


```yaml
key:
master-key:
storage:
content:
key : value
key2 : value2
```
7 changes: 6 additions & 1 deletion local-environment/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ mfa:
otp:
length: 6
timeToLiveInSeconds: 600
key:
master-key:
storage:
content:
key : "CrZKwm8YWGN5xYeKlaC9vXUBAFFzKYsqfaOFSrrqQgA="

document:
engine: file-system
Expand Down Expand Up @@ -141,4 +146,4 @@ spring:
password: postgres
config:
activate:
on-profile: experimental_database_persistence
on-profile: database
9 changes: 3 additions & 6 deletions local-environment/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,8 @@ cd ../../../../../communication/default/mail
cp * ../../../dist/mail/templates
```

## Postgres usage
## Installation in a NON AWS Environment

Postgres is an available option as storage, it is experimental right now, and it is supported only for account and roles.
Postgres and plain java key management is an available option

In order to activate it is needed to add the corresponding spring profile '''experimental_database_persistence''' and
for the init process add to the docker run the environment variable '''experimental_database_persistence=true'''with the command like below:

> docker run --pull=always -e experimental_database_persistence=true -it mrflick72/vauthenticator-local-tenant-installer:latest
In order to activate aws native service usage like KMS, DynamoDB and so on please use the spring profile '''aws''' default otherwise
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class AccountConfig {


@Bean("accountRepository")
@Profile("experimental_database_persistence")
@Profile("database")
fun jdbcAccountRepository(
jdbcTemplate: JdbcTemplate
) = JdbcAccountRepository(jdbcTemplate)
Expand All @@ -68,7 +68,7 @@ class AccountConfig {
havingValue = "false",
matchIfMissing = true
)
@Profile("!experimental_database_persistence")
@Profile("dynamo")
fun dynamoDbAccountRepository(
mapper: ObjectMapper,
dynamoDbClient: DynamoDbClient,
Expand All @@ -85,7 +85,7 @@ class AccountConfig {
havingValue = "true",
matchIfMissing = false
)
@Profile("!experimental_database_persistence")
@Profile("dynamo")
fun cachedDynamoDbAccountRepository(
mapper: ObjectMapper,
dynamoDbClient: DynamoDbClient,
Expand All @@ -106,7 +106,7 @@ class AccountConfig {
havingValue = "true",
matchIfMissing = false
)
@Profile("!experimental_database_persistence")
@Profile("dynamo")
fun accountCacheOperation(
redisTemplate: RedisTemplate<*, *>,
@Value("\${vauthenticator.dynamo-db.account.cache.ttl}") ttl: Duration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ class AuthorizationServerConfig {
}

@Bean("oAuth2AuthorizationService")
@Profile("!experimental_database_persistence")
@Profile("!database")
fun redisOAuth2AuthorizationService(redisTemplate: RedisTemplate<Any, Any>): OAuth2AuthorizationService {
return RedisOAuth2AuthorizationService(redisTemplate)
}


@Bean("oAuth2AuthorizationService")
@Profile("experimental_database_persistence")
@Profile("database")
fun jdbcOAuth2AuthorizationService(
jdbcTemplate : JdbcTemplate,
registeredClientRepository : RegisteredClientRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import org.springframework.context.annotation.Profile

@Configuration
@EnableAutoConfiguration(exclude = [DataSourceAutoConfiguration::class, DataSourceTransactionManagerAutoConfiguration::class, HibernateJpaAutoConfiguration::class])
@Profile("!experimental_database_persistence")
@Profile("aws")
class ExcludeDatabaseConfig

38 changes: 32 additions & 6 deletions src/main/kotlin/com/vauthenticator/server/keys/KeyConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.vauthenticator.server.keys.adapter.dynamo.DynamoDbKeyStorage
import com.vauthenticator.server.keys.adapter.jdbc.JdbcKeyStorage
import com.vauthenticator.server.keys.adapter.kms.KmsKeyDecrypter
import com.vauthenticator.server.keys.adapter.kms.KmsKeyGenerator
import com.vauthenticator.server.keys.adapter.java.*
import com.vauthenticator.server.keys.domain.*
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
Expand All @@ -18,14 +19,39 @@ import java.util.*
@Configuration(proxyBeanMethods = false)
class KeyConfig {

@Bean
fun keyGenerator(kmsClient: KmsClient): KeyGenerator = KmsKeyGenerator(kmsClient)
@Profile("kms")
@Bean("keyGenerator")
fun kmsKeyGenerator(kmsClient: KmsClient): KeyGenerator = KmsKeyGenerator(kmsClient)

@Bean
fun keyDecrypter(kmsClient: KmsClient): KeyDecrypter = KmsKeyDecrypter(kmsClient)
@Profile("!kms")
@Bean("keyGenerator")
fun JavaSecurityKeyGenerator(
kmsClient: KmsClient,
storage: KeyGeneratorMasterKeyStorage
): KeyGenerator = JavaSecurityKeyGenerator(
JavaSecurityCryptographicOperations(
KeyGeneratorMasterKeyRepository(storage)
)
)

@Profile("kms")
@Bean("keyDecrypter")
fun kmsKeyDecrypter(kmsClient: KmsClient): KeyDecrypter = KmsKeyDecrypter(kmsClient)

@Profile("!kms")
@Bean("keyDecrypter")
fun JavaSecurityKeyDecrypter(
@Value("\${key.master-key}") maserKid: String,
storage: KeyGeneratorMasterKeyStorage
): KeyDecrypter = JavaSecurityKeyDecrypter(
maserKid,
JavaSecurityCryptographicOperations(
KeyGeneratorMasterKeyRepository(storage)
)
)

@Bean("keyStorage")
@Profile("!experimental_database_persistence")
@Profile("dynamo")
fun dynamoDbKeyStorage(
clock: Clock,
dynamoDbClient: DynamoDbClient,
Expand All @@ -34,7 +60,7 @@ class KeyConfig {
) = DynamoDbKeyStorage(clock, dynamoDbClient, signatureTableName, mfaTableName)

@Bean("keyStorage")
@Profile("experimental_database_persistence")
@Profile("database")
fun jdbcKeyStorage(jdbcTemplate: JdbcTemplate, clock: Clock) = JdbcKeyStorage(jdbcTemplate, clock)

@Bean("keyRepository")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.vauthenticator.server.keys.adapter.java

import com.vauthenticator.server.extentions.decoder
import com.vauthenticator.server.keys.domain.MasterKid
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.Security
import java.security.spec.RSAKeyGenParameterSpec
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec


class JavaSecurityCryptographicOperations(
private val repository: KeyGeneratorMasterKeyRepository
) {
companion object {
init {
Security.addProvider(BouncyCastleProvider())
}
}

fun generateRSAKeyPair(): KeyPair {
val keyPair: KeyPair
try {
val keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC")
keyPairGenerator.initialize(RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
keyPair = keyPairGenerator.generateKeyPair()
} catch (ex: Exception) {
throw IllegalStateException(ex)
}
return keyPair
}

fun encryptKeyWith(masterKid: MasterKid, encodedPlainText: ByteArray): ByteArray {
val masterKey = decoder.decode(repository.maskerKeyFor(masterKid));
val key = SecretKeySpec(masterKey, "AES")
val cipher = Cipher.getInstance("AES")
cipher.init(Cipher.ENCRYPT_MODE, key)
return cipher.doFinal(encodedPlainText)
}

fun decryptKeyWith(masterKid: MasterKid, encodedEncryptedText: ByteArray): ByteArray {
val masterKey = decoder.decode(repository.maskerKeyFor(masterKid));
val key = SecretKeySpec(masterKey, "AES")
val cipher = Cipher.getInstance("AES")
cipher.init(Cipher.DECRYPT_MODE, key)
return cipher.doFinal(decoder.decode(encodedEncryptedText))
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.vauthenticator.server.keys.adapter.java

import com.vauthenticator.server.extentions.encoder
import com.vauthenticator.server.keys.domain.KeyDecrypter
import com.vauthenticator.server.keys.domain.MasterKid

class JavaSecurityKeyDecrypter(
private val maserKid: String,
private val javaSecurityCryptographicOperations: JavaSecurityCryptographicOperations
) : KeyDecrypter {
override fun decryptKey(encrypted: String): String {
return encoder.encode(javaSecurityCryptographicOperations.decryptKeyWith(MasterKid(maserKid), encrypted.toByteArray()))
.decodeToString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.vauthenticator.server.keys.adapter.java

import com.vauthenticator.server.keys.domain.DataKey
import com.vauthenticator.server.keys.domain.KeyGenerator
import com.vauthenticator.server.keys.domain.MasterKid
import java.util.*


class JavaSecurityKeyGenerator(
private val javaSecurityCryptographicOperations: JavaSecurityCryptographicOperations
) : KeyGenerator {


override fun dataKeyPairFor(masterKid: MasterKid): DataKey {
val generateRSAKeyPair = javaSecurityCryptographicOperations.generateRSAKeyPair()
return DataKey(
javaSecurityCryptographicOperations.encryptKeyWith(masterKid, generateRSAKeyPair.private.encoded),
Optional.of(generateRSAKeyPair.public.encoded)
)
}

override fun dataKeyFor(masterKid: MasterKid): DataKey {
val generateRSAKeyPair = javaSecurityCryptographicOperations.generateRSAKeyPair()
return DataKey(
javaSecurityCryptographicOperations.encryptKeyWith(masterKid, generateRSAKeyPair.private.encoded),
Optional.empty()
)
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.vauthenticator.server.keys.adapter.java

import com.vauthenticator.server.keys.domain.MasterKid
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile


class KeyGeneratorMasterKeyRepository(
val storage: KeyGeneratorMasterKeyStorage
) {

fun maskerKeyFor(masterKeyId: MasterKid): String {
return storage.content[masterKeyId.content()]!!
}

}

@Profile("!kms")
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(KeyGeneratorMasterKeyStorage::class)
class KeyGeneratorMasterKeyRepositoryConfig {

}

@ConfigurationProperties(prefix = "key.master-key.storage")
data class KeyGeneratorMasterKeyStorage(val content: Map<String, String>) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.vauthenticator.server.keys.adapter.java

import com.vauthenticator.server.keys.domain.*
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Service

@Service
@Profile("!kms")
class KeyInitJob(
@Value("\${key.master-key}") private val maserKid: String,
private val keyStorage: KeyStorage,
private val keyRepository: KeyRepository
) : ApplicationRunner {

override fun run(args: ApplicationArguments) {

if (keyStorage.signatureKeys().keys.isEmpty()) {
val kid = keyRepository.createKeyFrom(
masterKid = MasterKid(maserKid),
keyPurpose = KeyPurpose.SIGNATURE,
keyType = KeyType.ASYMMETRIC,
)
println(kid)
}

}

}
4 changes: 2 additions & 2 deletions src/main/kotlin/com/vauthenticator/server/mfa/MfaConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import java.util.*
class MfaConfig {

@Bean("mfaAccountMethodsRepository")
@Profile("!experimental_database_persistence")
@Profile("dynamo")
fun dynamoDbMfaAccountMethodsRepository(
keyRepository: KeyRepository,
dynamoDbClient: DynamoDbClient,
Expand All @@ -52,7 +52,7 @@ class MfaConfig {
) { MfaDeviceId(UUID.randomUUID().toString()) }

@Bean("mfaAccountMethodsRepository")
@Profile("experimental_database_persistence")
@Profile("database")
fun jdbcMfaAccountMethodsRepository(
keyRepository: KeyRepository,
jdbcTemplate: JdbcTemplate,
Expand Down
Loading

0 comments on commit 733e527

Please sign in to comment.