API 1.0.37+2 SSL, password encryption, TrainingPlanTranslation

This commit is contained in:
Bossanyi Tibor
2021-05-12 20:46:10 +02:00
parent a38c244d79
commit 7d78aa38fb
20 changed files with 540 additions and 17 deletions
@@ -1,12 +1,33 @@
package com.aitrainer.api
import org.jasypt.encryption.StringEncryptor
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig
import org.slf4j.LoggerFactory
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder
import org.springframework.context.annotation.Bean
@SpringBootApplication
class ApiApplication
class ApiApplication {
@Bean(name = ["jasyptStringEncryptor"])
fun stringEncryptor(): StringEncryptor? {
val encryptor = PooledPBEStringEncryptor()
val config = SimpleStringPBEConfig()
config.password = "Tibor"
config.algorithm = "PBEWithMD5AndDES"
config.setKeyObtentionIterations("1000")
config.setPoolSize("1")
config.providerName = "SunJCE"
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator")
config.stringOutputType = "base64"
encryptor.setConfig(config)
return encryptor
}
}
private val logger = LoggerFactory.getLogger(ApiApplication::class.simpleName)
@Override
@@ -16,6 +37,10 @@ class ApiApplication
fun main(args: Array<String>) {
logger.info(" ---- Start aitrainer API")
SpringApplication.run(ApiApplication::class.java, *args)
val app = SpringApplication(ApiApplication::class.java)
app.setAdditionalProfiles("ssl")
app.run(*args)
}
@@ -18,7 +18,9 @@ class CustomerPackageController( private val customerRepository: CustomerReposit
private val purchaseRepository: PurchaseRepository,
private val customerPropertyRepository: CustomerPropertyRepository,
private val exerciseResultRepository: ExerciseResultRepository,
private val customerActivityRepository: CustomerActivityRepository) {
private val customerActivityRepository: CustomerActivityRepository,
private val customerTrainingPlanRepository: CustomerTrainingPlanRepository
) {
@GetMapping("/app_customer_package/{id}")
fun getCustomerPackageData(@PathVariable(value = "id") customerId: Long): ResponseEntity<String> {
@@ -60,6 +62,9 @@ class CustomerPackageController( private val customerRepository: CustomerReposit
val listActivityResult = customerActivityRepository.findByCustomerId(customerId)
val listActivityJson = gson.toJson(listActivityResult)
val listTrainingPlan = customerTrainingPlanRepository.findAllByCustomerId(customerId)
val listTrainingPlanJson = gson.toJson(listTrainingPlan)
val packageJson: String =
getClassRecord(Customer::class.simpleName, customerJson) +
"|||" + getClassRecord(CustomerExerciseDevice::class.simpleName, listCustomerExerciseDeviceJson) +
@@ -69,7 +74,8 @@ class CustomerPackageController( private val customerRepository: CustomerReposit
"|||" + getClassRecord(CustomerProperty::class.simpleName+"All", listCustomerPropertyAllJson) +
"|||" + getClassRecord(CustomerProperty::class.simpleName, listCustomerPropertyJson) +
"|||" + getClassRecord(ExerciseResult::class.simpleName, listExerciseResultJson+
"|||" + getClassRecord(CustomerActivity::class.simpleName, listActivityJson))
"|||" + getClassRecord(CustomerActivity::class.simpleName, listActivityJson) +
"|||" + getClassRecord(CustomerTrainingPlan::class.simpleName, listTrainingPlanJson))
return if (packageJson.isEmpty()) ResponseEntity.notFound().build() else
ResponseEntity.ok().body(packageJson)
@@ -0,0 +1,38 @@
package com.aitrainer.api.controller
import com.aitrainer.api.model.CustomerProperty
import com.aitrainer.api.model.CustomerTrainingPlan
import com.aitrainer.api.repository.CustomerPropertyRepository
import com.aitrainer.api.repository.CustomerTrainingPlanRepository
import org.slf4j.LoggerFactory
import org.springframework.http.ResponseEntity
import org.springframework.security.access.annotation.Secured
import org.springframework.web.bind.annotation.*
import javax.validation.Valid
@RestController
@RequestMapping("/api")
class CustomerTrainingPlanController(private val customerTrainingPlanRepository: CustomerTrainingPlanRepository) {
private val logger = LoggerFactory.getLogger(javaClass)
@PostMapping("/customer_training_plan")
fun createNewCustomerTrainingPlan(@Valid @RequestBody customerTrainingPlan: CustomerTrainingPlan): ResponseEntity<CustomerTrainingPlan> {
logger.info("Create customer property: $customerTrainingPlan")
return ResponseEntity.ok().body(customerTrainingPlanRepository.save(customerTrainingPlan))
}
@Secured
@PostMapping("customer_training_plan/update/{id}")
fun updateCustomerProperty(@PathVariable(value = "id") trainingPlanId: Long,
@Valid @RequestBody customerTrainingPlanToUpdate: CustomerTrainingPlan): ResponseEntity<CustomerTrainingPlan> {
val customerTrainingPlan = customerTrainingPlanRepository.findById(trainingPlanId).orElse(null)
?: return ResponseEntity.notFound().build()
val updatedCustomerTrainingPlan = customerTrainingPlan.copy(
trainingPlanId = customerTrainingPlanToUpdate.trainingPlanId,
customerId = customerTrainingPlanToUpdate.customerId,
dateAdd = customerTrainingPlanToUpdate.dateAdd
)
return ResponseEntity.ok().body(customerTrainingPlanRepository.save(updatedCustomerTrainingPlan))
}
}
@@ -0,0 +1,16 @@
package com.aitrainer.api.model
import com.google.gson.annotations.Expose
import org.springframework.lang.NonNull
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
@Entity
data class CustomerTrainingPlan (
@Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @get: NonNull var customerTrainingPlanId: Long = 0,
@Expose @get: NonNull var customerId: Long = 0,
@Expose @get: NonNull var trainingPlanId: Long = 0,
@Expose @get: NonNull var dateAdd: String? = null
)
@@ -11,10 +11,15 @@ import javax.validation.constraints.NotBlank
data class TrainingPlan (
@Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @get: NonNull var trainingPlanId: Long = 0,
@Expose @get: NotBlank var name: String = "",
@Expose var description: String?,
@Expose @get: NotBlank var type: String = "",
) {
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.EAGER, mappedBy = "trainingPlan")
@Fetch(value = FetchMode.SUBSELECT)
@Expose val details: List<TrainingPlanDetail> = mutableListOf<TrainingPlanDetail>().toList()
@OneToMany(cascade = [(CascadeType.ALL)], fetch = FetchType.EAGER, mappedBy = "trainingPlan")
@Fetch(value = FetchMode.SUBSELECT)
@Expose val details: List<TrainingPlanDetail> = mutableListOf<TrainingPlanDetail>().toList()
@OneToMany(fetch = FetchType.EAGER, mappedBy = "trainingPlan")
@Fetch(value = FetchMode.SUBSELECT)
@Expose val translations: List<TrainingPlanTranslation> = mutableListOf<TrainingPlanTranslation>()
}
@@ -11,11 +11,11 @@ data class TrainingPlanDetail (
@Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @get: NonNull var trainingPlanDetailId: Long = 0,
@Expose @get: NotBlank var exerciseTypeId: Int = 0,
@Expose @get: NotBlank var sort: Int = 0,
@Expose @get: NotBlank var set: Int = 0,
@Expose @get: NotBlank var repeats: Int = 0,
@Expose @get: NotBlank var weight: Double = 0.0,
@Expose @get: NotBlank var restingTime: Int = 0,
@Expose @get: NotBlank var parallel: Boolean = false,
@Expose var set: Int = 0,
@Expose var repeats: Int? = 0,
@Expose var weight: Double? = 0.0,
@Expose var restingTime: Int? = 0,
@Expose var parallel: Boolean? = false,
@Expose var day: String? = null,
) {
@@ -24,7 +24,4 @@ data class TrainingPlanDetail (
@JoinColumn(name = "trainingPlanId", nullable = false)
@JsonIgnore
val trainingPlan: TrainingPlan? = null
}
@@ -0,0 +1,23 @@
package com.aitrainer.api.model
import com.fasterxml.jackson.annotation.JsonIgnore
import com.google.gson.annotations.Expose
import javax.persistence.*
import javax.validation.constraints.NotBlank
@Entity
data class TrainingPlanTranslation (
@Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val translationId: Long = 0,
@Expose @get: NotBlank var languageCode: String?,
@Expose @get: NotBlank var nameTranslation: String = "",
@Expose @get: NotBlank var descriptionTranslation: String = "",
) {
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "trainingPlanId", nullable = false)
@JsonIgnore
val trainingPlan: TrainingPlan? = null
}
@@ -0,0 +1,10 @@
package com.aitrainer.api.repository
import com.aitrainer.api.model.CustomerTrainingPlan
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface CustomerTrainingPlanRepository: JpaRepository<CustomerTrainingPlan, Long> {
fun findAllByCustomerId(customerId: Long): List<CustomerTrainingPlan>
}
@@ -0,0 +1,9 @@
package com.aitrainer.api.service
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.PropertySource
@Configuration
@PropertySource("classpath:application.ssl.properties")
class AppConfigForJasyptStarter {
}
@@ -0,0 +1,24 @@
package com.aitrainer.api.service
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.env.Environment
import org.springframework.stereotype.Service
@Service
class PropertyServiceForJasyptStarter {
@Value("\${trust.store.password}")
val storePassword: String? = null
@Value("\${server.ssl.key-store-password}")
val keyStorePassword: String? = null
fun getPasswordUsingEnvironment(environment: Environment): String? {
return environment.getProperty("trust.store.password")
}
fun getKeyStorePasswordUsingEnvironment(environment: Environment): String? {
return environment.getProperty("server.ssl.key-store-password")
}
}
+2 -1
View File
@@ -19,4 +19,5 @@ logging.file=logs
# if the database structure has been changed, increment this version number
application.version=1.0.37
jwt.secret=aitrainer
jwt.secret=aitrainer
jasypt.encryptor.password=Tibor
@@ -0,0 +1,19 @@
http.port=8088
server.port=8443
server.ssl.enabled=true
jasypt.encryptor.bean=jasyptStringEncryptor
# The format used for the keystore. It could be set to JKS in case it is a JKS file
server.ssl.key-store-type=PKCS12
# The path to the keystore containing the certificate
server.ssl.key-store=classpath:keystore/aitrainer_server.p12
# The password used to generate the certificate
server.ssl.key-store-password=ENC(aI6iP8wGJ9dwIA+hKabwK/VDXe4NOrhl)
# The alias mapped to the certificate
server.ssl.key-alias=aitrainer_server
#trust store location
trust.store=classpath:keystore/aitrainer_server.p12
#trust store password
trust.store.password=ENC(aI6iP8wGJ9dwIA+hKabwK/VDXe4NOrhl)
Binary file not shown.
@@ -42,13 +42,16 @@ class AppCustomerPackageTest {
@Autowired
private lateinit var customerActivityRepository: CustomerActivityRepository
@Autowired
private lateinit var customerTrainingPlanRepository: CustomerTrainingPlanRepository
@Test
fun customerPackageTest() {
val gson = Gson()
val controller = CustomerPackageController(customerRepository, customerExerciseDeviceRepository,
exercisesRepository, productTestRepository, purchaseRepository, customerPropertyRepository,
exerciseResultRepository, customerActivityRepository )
exerciseResultRepository, customerActivityRepository, customerTrainingPlanRepository )
var response: ResponseEntity<*> = controller.getCustomerPackageData(91)
assertEquals(response.statusCode, HttpStatus.NOT_FOUND)
@@ -118,6 +121,14 @@ class AppCustomerPackageTest {
assertTrue(activityList.isNotEmpty())
assertEquals(activityList[0].type,"basic_tutorial")
}
record[0] == CustomerTrainingPlan::class.simpleName+"All" -> {
val trainingPlanJson: String = record[1]
val type = object : TypeToken<List<CustomerTrainingPlan?>?>() {}.type
val trainingPlanList: List<CustomerTrainingPlan> = gson.fromJson(trainingPlanJson, type)
assertTrue(trainingPlanList.isNotEmpty())
assertEquals(trainingPlanList[0].customerId,91)
assertEquals(trainingPlanList[0].trainingPlanId,2)
}
}
}
@@ -0,0 +1,44 @@
package com.aitrainer.api.test
import com.aitrainer.api.controller.CustomerTrainingPlanController
import com.aitrainer.api.model.CustomerTrainingPlan
import com.aitrainer.api.repository.CustomerTrainingPlanRepository
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CustomerTrainingPlanTest {
@Autowired
private lateinit var customerTrainingPlanRepository: CustomerTrainingPlanRepository
@Test
fun testInsert() {
val customerTrainingPlan = CustomerTrainingPlan(
customerId = 90,
trainingPlanId = 1,
dateAdd = "2021-05-12",
)
val controller = CustomerTrainingPlanController(customerTrainingPlanRepository)
val response = controller.createNewCustomerTrainingPlan(customerTrainingPlan)
val newCustomerTrainingPlan = response.body
assertTrue(newCustomerTrainingPlan is CustomerTrainingPlan)
assertEquals(newCustomerTrainingPlan.dateAdd, "2021-05-12")
customerTrainingPlanRepository.delete(newCustomerTrainingPlan)
}
}
@@ -0,0 +1,120 @@
package com.aitrainer.api.test
import com.aitrainer.api.ApiApplication
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.core.io.Resource
import org.springframework.http.HttpStatus
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.junit4.SpringRunner
import java.io.FileInputStream
import java.net.URI
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.time.Duration
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import kotlin.test.assertEquals
import kotlin.test.assertTrue
/*@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@RunWith(SpringRunner::class)
@SpringBootTest(classes = [ApiApplication::class], webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ActiveProfiles("ssl")*/
class HttpsApplicationIntegrationTest {
/* @Value("\${trust.store}")
@Autowired
private lateinit var trustStore: Resource
@Value("\${trust.store.password}")
@Autowired
private lateinit var trustStorePassword: String*/
/*
fun buildClient(): HttpClient? {
val ks = KeyStore.getInstance("pkcs12")
val kmf = KeyManagerFactory.getInstance("SunX509")
//create new ssl context
val sslContext = SSLContext.getInstance("SSL")
//load your file to keystore
ks.load(FileInputStream(trustStore.file.toString()), trustStorePassword.toCharArray())
kmf.init(ks, trustStorePassword.toCharArray())
//set global system properties
System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol")
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12")
System.setProperty("javax.net.ssl.keyStore", trustStorePassword)
System.setProperty("javax.net.ssl.keyStorePassword", trustStorePassword)
//initialize sslContext
sslContext.init(kmf.keyManagers, getTrustAllCert(), null)
val client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.sslContext(sslContext)
.build()
//you can send request with your ssl certificate
return client
}
private fun getTrustAllCert(): Array<TrustManager> {
return arrayOf(object : javax.net.ssl.X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate>? = null
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
})
}
@Test
fun testSSL() {
val httpClient: HttpClient? = buildClient()
if ( httpClient == null) {
assertTrue(false)
}
val request = HttpRequest.newBuilder()
.uri(URI.create(WELCOME_URL))
.build()
val response = httpClient!!.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(response.statusCode(), HttpStatus.OK)
println(response.body())
}
*/
/* @Test
@Throws(Exception::class)
fun whenGETanHTTPSResource_thenCorrectResponse() {
val response = restTemplate().getForEntity(
WELCOME_URL,
String::class.java, Collections.emptyMap()
)
assertEquals("<h1>Welcome to Secured Site</h1>", response.body)
assertEquals(HttpStatus.OK, response.statusCode)
}
@Throws(Exception::class)
fun restTemplate(): RestTemplate {
val sslContext: SSLContext =
SSLContextBuilder().loadTrustMaterial(trustStore.getURL(), trustStorePassword!!.toCharArray())
.build()
val socketFactory = SSLConnectionSocketFactory(sslContext)
val httpClient: HttpClient = HttpClients.custom()
.setSSLSocketFactory(socketFactory)
.build()
val factory = HttpComponentsClientHttpRequestFactory(httpClient)
return RestTemplate(factory)
}*/
companion object {
private const val WELCOME_URL = "https://localhost:8443/api/welcome"
}
}
@@ -0,0 +1,57 @@
package com.aitrainer.api.test
import com.aitrainer.api.service.PropertyServiceForJasyptStarter
import org.jasypt.encryption.StringEncryptor
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.ApplicationContext
import org.springframework.core.env.Environment
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class JasyptTest {
@Autowired
private lateinit var appCtx: ApplicationContext
@Test
fun testPassword() {
val passwordEncryptor = getEncryptor()
val encryptedPassword = passwordEncryptor.encrypt("xxx")
println(encryptedPassword)
assertFalse("xxx" == encryptedPassword);
}
fun getEncryptor(): StringEncryptor {
val encryptor = PooledPBEStringEncryptor()
val config = SimpleStringPBEConfig()
config.password = "Tibor"
config.algorithm = "PBEWithMD5AndDES"
config.setKeyObtentionIterations("1000")
config.setPoolSize("1")
config.providerName = "SunJCE"
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator")
config.stringOutputType = "base64"
encryptor.setConfig(config)
return encryptor
}
//@Test
fun whenDecryptedPasswordNeeded_GetFromService() {
System.setProperty("jasypt.encryptor.password", "Tibor")
val service: PropertyServiceForJasyptStarter = appCtx
.getBean(PropertyServiceForJasyptStarter::class.java)
assertEquals("xxx", service.storePassword)
val environment: Environment = appCtx.getBean(Environment::class.java)
assertEquals(
"xxx",
service.getPasswordUsingEnvironment(environment)
)
}
}