feat: As a developer, I want to have spring security added to the reporting microservice

This commit is contained in:
Marcel Haag 2023-02-15 08:28:01 +01:00
parent 88b3647295
commit b7c04f4bcb
16 changed files with 274 additions and 67 deletions

View File

@ -63,10 +63,10 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux") implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-actuator") implementation("org.springframework.boot:spring-boot-starter-actuator")
/*implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client") implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server") implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
api("org.springframework.security:spring-security-jwt:1.1.1.RELEASE")*/ api("org.springframework.security:spring-security-jwt:1.1.1.RELEASE")
/* Reporting */ /* Reporting */
implementation("net.sf.jasperreports:jasperreports:6.20.0") implementation("net.sf.jasperreports:jasperreports:6.20.0")

View File

@ -15,12 +15,12 @@
"method": "GET", "method": "GET",
"header": [], "header": [],
"url": { "url": {
"raw": "http://localhost:8888/auth/realms/c4po_realm_local/.well-known/openid-configuration", "raw": "http://localhost:8080/auth/realms/c4po_realm_local/.well-known/openid-configuration",
"protocol": "http", "protocol": "http",
"host": [ "host": [
"localhost" "localhost"
], ],
"port": "8888", "port": "8080",
"path": [ "path": [
"auth", "auth",
"realms", "realms",
@ -75,12 +75,12 @@
] ]
}, },
"url": { "url": {
"raw": "http://localhost:8888/auth/realms/c4po_realm_local/protocol/openid-connect/token", "raw": "http://localhost:8080/auth/realms/c4po_realm_local/protocol/openid-connect/token",
"protocol": "http", "protocol": "http",
"host": [ "host": [
"localhost" "localhost"
], ],
"port": "8888", "port": "8080",
"path": [ "path": [
"auth", "auth",
"realms", "realms",

View File

@ -1,12 +1,11 @@
package com.securityc4po.reporting.configuration.security package com.securityc4po.reporting.configuration.security
class Appuser {}
/*import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails import org.springframework.security.core.userdetails.UserDetails
import java.util.stream.Collectors import java.util.stream.Collectors
class Appuser internal constructor(val token: String) : UserDetails { class Appuser internal constructor() : UserDetails {
override fun getAuthorities(): Collection<GrantedAuthority> { override fun getAuthorities(): Collection<GrantedAuthority> {
return listOf("user").stream().map { return listOf("user").stream().map {
@ -21,7 +20,7 @@ class Appuser internal constructor(val token: String) : UserDetails {
override fun getPassword(): String { override fun getPassword(): String {
return "n/a" return "n/a"
} }
d
override fun getUsername(): String { override fun getUsername(): String {
return "n/a" return "n/a"
} }
@ -45,4 +44,4 @@ class Appuser internal constructor(val token: String) : UserDetails {
companion object { companion object {
private val ROLE_PREFIX = "ROLE_" private val ROLE_PREFIX = "ROLE_"
} }
}*/ }

View File

@ -0,0 +1,69 @@
package com.securityc4po.reporting.configuration.security
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.springframework.core.convert.converter.Converter
import org.springframework.security.authentication.AbstractAuthenticationToken
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.oauth2.jwt.Jwt
import reactor.core.publisher.Mono
import java.util.stream.Collectors
class AppuserJwtAuthConverter(private val appuserDetailsService: UserAccountDetailsService) :
Converter<Jwt, Mono<AbstractAuthenticationToken>> {
override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> {
val authorities = extractAuthorities(jwt)
// val sub = extractSub(jwt)
val username = extractUserName(jwt)
return appuserDetailsService
.findByUsername(username)
.map { user ->
UsernamePasswordAuthenticationToken(user, "n/a", authorities);
}
}
private fun extractSub(jwt: Jwt): String {
val sub = jwt.getClaims().get(SUB).toString()
if (sub.isEmpty() || sub.equals("null")) {
return "n/a"
}
return sub
}
private fun extractUserName(jwt: Jwt): String {
val username = jwt.getClaims().get(USERNAME).toString()
if (username.isEmpty() || username.equals("null")) {
return "n/a"
}
return username
}
private fun extractAuthorities(jwt: Jwt): Collection<GrantedAuthority> {
return this.getScopes(jwt).stream().map { authority ->
ROLE_PREFIX + authority.toUpperCase()
}.map {
SimpleGrantedAuthority(it)
}.collect(Collectors.toList())
}
private fun getScopes(jwt: Jwt): Collection<String> {
val mapper = ObjectMapper()
val scopes = jwt.getClaims().get(GROUPS_CLAIM).toString()
val roleStringValue = mapper.readTree(scopes).get("roles").toString()
val roles = mapper.readValue<Collection<String>>(roleStringValue)
if (!roles.isEmpty()) {
return roles
}
return emptyList()
}
companion object {
private val GROUPS_CLAIM = "realm_access"
private val ROLE_PREFIX = "ROLE_"
private val SUB = "sub"
private val USERNAME = "username"
}
}

View File

@ -0,0 +1,17 @@
package com.securityc4po.reporting.configuration.security
import org.modelmapper.ModelMapper
import org.modelmapper.convention.MatchingStrategies
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class ModelmapperCfg {
@Bean
fun modelMapper(): ModelMapper {
val modelMapper = ModelMapper()
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT)
return modelMapper
}
}

View File

@ -0,0 +1,14 @@
package com.securityc4po.reporting.configuration.security
import org.springframework.security.core.userdetails.ReactiveUserDetailsService
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
@Service
class UserAccountDetailsService : ReactiveUserDetailsService {
override fun findByUsername(username: String): Mono<UserDetails> {
return Appuser().toMono()
}
}

View File

@ -0,0 +1,73 @@
package com.securityc4po.reporting.configuration.security
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.http.HttpMethod
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.oauth2.core.OAuth2TokenValidator
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.JwtValidators
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoders
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.web.cors.CorsConfiguration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
@ComponentScan
class WebSecurityConfiguration(private val userAccountDetailsService: UserAccountDetailsService) {
@Value("\${external.issuer-uri}")
var externalIssuerUri: String? = null
@Value("\${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
var internalIssuerUri: String? = null
@Bean
fun setSecurityWebFilterChains(http: ServerHttpSecurity): SecurityWebFilterChain {
http.cors().configurationSource {
CorsConfiguration().apply {
this.applyPermitDefaultValues()
this.addAllowedMethod(HttpMethod.DELETE)
this.addAllowedMethod(HttpMethod.PATCH)
this.addAllowedMethod(HttpMethod.POST)
this.addAllowedMethod(HttpMethod.GET)
this.addAllowedMethod(HttpMethod.PUT)
}
}
.and()
.csrf()
.disable()
.authorizeExchange()
.pathMatchers(HttpMethod.GET, "/v1/reports/**").authenticated()
.pathMatchers("/actuator/**").permitAll()
/*.pathMatchers("/docs/SecurityC4PO.html").permitAll()*/
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(appuserJwtAuthenticationConverter())
return http.build()
}
@Bean
fun appuserJwtAuthenticationConverter(): AppuserJwtAuthConverter {
return AppuserJwtAuthConverter(userAccountDetailsService)
}
@Bean
@Profile("COMPOSE")
fun jwtDecoder(): ReactiveJwtDecoder {
val jwtDecoder = ReactiveJwtDecoders.fromIssuerLocation(internalIssuerUri) as NimbusReactiveJwtDecoder
val withIssuer: OAuth2TokenValidator<Jwt> = JwtValidators.createDefaultWithIssuer(externalIssuerUri)
jwtDecoder.setJwtValidator(withIssuer)
return jwtDecoder
}
}

View File

@ -2,19 +2,19 @@ package com.securityc4po.reporting.report
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.securityc4po.reporting.configuration.security.Appuser
import com.securityc4po.reporting.extensions.getLoggerFor import com.securityc4po.reporting.extensions.getLoggerFor
import com.securityc4po.reporting.remote.APIService import com.securityc4po.reporting.remote.APIService
import com.securityc4po.reporting.remote.model.ProjectReport import com.securityc4po.reporting.remote.model.ProjectReport
import org.apache.pdfbox.io.IOUtils import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.http.ResponseEntity.notFound import org.springframework.http.ResponseEntity.notFound
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty import reactor.kotlin.core.publisher.switchIfEmpty
import java.io.File import java.io.File
// import com.securityc4po.reporting.configuration.security.Appuser
// import org.springframework.security.core.annotation.AuthenticationPrincipal
@RestController @RestController
@RequestMapping("/reports") @RequestMapping("/reports")
@ -32,18 +32,16 @@ class ReportController(private val apiService: APIService, private val reportSer
"/{projectId}/pdf", "/{projectId}/pdf",
produces = [MediaType.APPLICATION_PDF_VALUE] produces = [MediaType.APPLICATION_PDF_VALUE]
) )
fun downloadPentestReportPDF(@PathVariable(value = "projectId") projectId: String/*, @AuthenticationPrincipal user: Appuser*/): Mono<ResponseEntity<ByteArray>> { fun downloadPentestReportPDF(@PathVariable(value = "projectId") projectId: String, @AuthenticationPrincipal user: Appuser): Mono<ResponseEntity<ByteArray>> {
// Todo: Create Report with Jasper // Todo: Create Report with Jasper
// this.apiService.requestProjectDataById(projectId, user.token) // this.apiService.requestProjectDataById(projectId, user.token)
val jsonProjectReportString: String = val jsonProjectReportString: String =
File("./src/test/resources/ProjectReportData.json").readText(Charsets.UTF_8) File("./src/test/resources/ProjectReportData.json").readText(Charsets.UTF_8)
val jsonProjectReportCollection: ProjectReport = val jsonProjectReportCollection: ProjectReport =
jacksonObjectMapper().readValue<ProjectReport>(jsonProjectReportString) jacksonObjectMapper().readValue<ProjectReport>(jsonProjectReportString)
return this.reportService.createReport(jsonProjectReportCollection, "pdf").map { reportClassLoaderFilePatch -> // Setup headers
val reportRessourceStream = ReportController::class.java.getResourceAsStream(reportClassLoaderFilePatch) return this.reportService.createReport(jsonProjectReportCollection, "pdf").map { reportClassLoaderFilePath ->
// Todo: Fix Error with IOUtils.toByteArray(reportRessourceStream) on first start of application ResponseEntity.ok().body(reportClassLoaderFilePath)
val response = IOUtils.toByteArray(reportRessourceStream)
ResponseEntity.ok().body(response)
}.switchIfEmpty { }.switchIfEmpty {
Mono.just(notFound().build<ByteArray>()) Mono.just(notFound().build<ByteArray>())
}.doOnSuccess { }.doOnSuccess {

View File

@ -5,13 +5,16 @@ import com.securityc4po.reporting.remote.model.*
import net.sf.jasperreports.engine.* import net.sf.jasperreports.engine.*
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.apache.pdfbox.io.MemoryUsageSetting
import org.apache.pdfbox.multipdf.PDFMergerUtility import org.apache.pdfbox.multipdf.PDFMergerUtility
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.util.ResourceUtils import org.springframework.util.ResourceUtils
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
@Service @Service
/* /*
* This Service makes use of the created Jasper Templates * This Service makes use of the created Jasper Templates
@ -23,8 +26,10 @@ class ReportService {
private val reportCoverDesignTemplate = "./src/main/resources/jasper/reports/c4po_cover.jrxml" private val reportCoverDesignTemplate = "./src/main/resources/jasper/reports/c4po_cover.jrxml"
private val reportContentDesignTemplate = "./src/main/resources/jasper/reports/c4po_content.jrxml" private val reportContentDesignTemplate = "./src/main/resources/jasper/reports/c4po_content.jrxml"
private val reportStateOfConfidentialityDesignTemplate = "./src/main/resources/jasper/reports/c4po_state_of_confidentiality.jrxml" private val reportStateOfConfidentialityDesignTemplate =
private val reportExecutiveSummaryDesignTemplate = "./src/main/resources/jasper/reports/c4po_executive_summary.jrxml" "./src/main/resources/jasper/reports/c4po_state_of_confidentiality.jrxml"
private val reportExecutiveSummaryDesignTemplate =
"./src/main/resources/jasper/reports/c4po_executive_summary.jrxml"
private val reportPentestsDesignTemplate = "./src/main/resources/jasper/reports/c4po_pentests.jrxml" private val reportPentestsDesignTemplate = "./src/main/resources/jasper/reports/c4po_pentests.jrxml"
private val reportAppendenciesDesignTemplate = "./src/main/resources/jasper/reports/c4po_appendencies.jrxml" private val reportAppendenciesDesignTemplate = "./src/main/resources/jasper/reports/c4po_appendencies.jrxml"
@ -33,39 +38,31 @@ class ReportService {
// Path where the created Reports are saved // Path where the created Reports are saved
private val reportDestination = "./src/main/resources/jasper/reportPDFs/" private val reportDestination = "./src/main/resources/jasper/reportPDFs/"
// Path where the completed Report is saved // Path where the completed Report is saved
private val reportFileDestination = "./src/main/resources/jasper/finalReport/" private val reportFileDestination = "./src/main/resources/jasper/finalReport/"
// Path where the completed Report can be found by class loader // Path where the completed Report can be found by class loader
private val reportFileForClassLoader = "/jasper/finalReport/" private val reportFileForClassLoader = "/jasper/finalReport/"
fun createReport(projectReportCollection: ProjectReport, reportFormat: String): Mono<String> { fun createReport(projectReportCollection: ProjectReport, reportFormat: String): Mono<ByteArray> {
val C4POPentestReport: PDFMergerUtility = PDFMergerUtility() // Setup Filepath destination
val reportFilePathDestination: String = reportFileDestination + projectReportCollection.title.replace(" ", "_") + "_report.pdf" val reportFilePathDestination: String =
val reportFilePathForClassLoader: String = reportFileForClassLoader + projectReportCollection.title.replace(" ", "_") + "_report.pdf" reportFileDestination + projectReportCollection.title.replace(" ", "_") + "_report.pdf"
// Setup PDFMergerUtility
val mergedC4POPentestReport: PDFMergerUtility = PDFMergerUtility()
// Setup ByteArrayOutputStream for "on the fly" file generation
val pdfDocOutputstream = ByteArrayOutputStream()
// Try to create report files & merge them together // Try to create report files & merge them together
return try { return createPentestReportFiles(projectReportCollection, reportFormat, mergedC4POPentestReport).collectList().map {
// Create report files // Merge report files
val coverFile: File = createCover(projectReportCollection, reportFormat) mergedC4POPentestReport.destinationFileName = reportFilePathDestination
val contentFile: File = createTableOfContent(projectReportCollection, reportFormat) mergedC4POPentestReport.destinationStream = pdfDocOutputstream
val confidentialityFile: File = createStateOfConfidentiality(projectReportCollection, reportFormat) mergedC4POPentestReport.mergeDocuments(MemoryUsageSetting.setupTempFileOnly())
val summaryFile: File = createExecutiveSummary(projectReportCollection, reportFormat) }.flatMap {
val pentestFiles: List<File> = createPentestReports(projectReportCollection, reportFormat) return@flatMap Mono.just(pdfDocOutputstream.toByteArray())
val appendenciesFile: File = createAppendencies(reportFormat) }.doOnError {
// Add files to [C4POPentestReport]
C4POPentestReport.addSource(coverFile)
C4POPentestReport.addSource(contentFile)
C4POPentestReport.addSource(confidentialityFile)
C4POPentestReport.addSource(summaryFile)
// Merge every Pentestreport file in List of File
pentestFiles.forEach { pentestFile -> C4POPentestReport.addSource(pentestFile) }
C4POPentestReport.addSource(appendenciesFile)
// Save completed report
C4POPentestReport.destinationFileName = reportFilePathDestination
C4POPentestReport.mergeDocuments()
reportFilePathForClassLoader.toMono()
} catch (e: Exception) {
logger.error("Report generation failed.") logger.error("Report generation failed.")
Mono.just("")
} }
} }
@ -78,7 +75,32 @@ class ReportService {
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Report file cleanup failed with exception: ", e) logger.error("Report file cleanup failed with exception: ", e)
} }
}
private fun createPentestReportFiles(
projectReportCollection: ProjectReport,
reportFormat: String,
mergedC4POPentestReport: PDFMergerUtility
): Flux<Unit> {
return Flux.just(
// Create report files
createCover(projectReportCollection, reportFormat),
createTableOfContent(projectReportCollection, reportFormat),
createStateOfConfidentiality(projectReportCollection, reportFormat),
createExecutiveSummary(projectReportCollection, reportFormat),
createPentestReports(projectReportCollection, reportFormat),
createAppendencies(reportFormat)
).map { jasperObject ->
if (jasperObject is File) {
mergedC4POPentestReport.addSource(jasperObject)
} else if (jasperObject is List<*>) {
jasperObject.forEach { jasperFile ->
if (jasperFile is File) {
mergedC4POPentestReport.addSource(jasperFile)
}
}
}
}
} }
private fun createCover(projectReportCollection: ProjectReport, reportFormat: String): File { private fun createCover(projectReportCollection: ProjectReport, reportFormat: String): File {
@ -284,7 +306,7 @@ class ReportService {
// Create File // Create File
var finalFile: File = File(reportDefaultPdf) var finalFile: File = File(reportDefaultPdf)
return if (reportFormat.equals("pdf")) { return if (reportFormat.equals("pdf")) {
JasperExportManager.exportReportToPdfFile(jasperPrintContent,reportDestination + "D_ExecutiveSummary.pdf") JasperExportManager.exportReportToPdfFile(jasperPrintContent, reportDestination + "D_ExecutiveSummary.pdf")
finalFile = File(reportDestination + "D_ExecutiveSummary.pdf") finalFile = File(reportDestination + "D_ExecutiveSummary.pdf")
finalFile finalFile
} else { } else {
@ -311,16 +333,18 @@ class ReportService {
// val pentestCommentsDataSource = // val pentestCommentsDataSource =
// Setup Parameter & add Sub-datasets // Setup Parameter & add Sub-datasets
val parameters = HashMap<String, Any>() val parameters = HashMap<String, Any>()
parameters["PentestFindingsDataSource"] = if (projectReportCollection.projectPentestReport[i].findings.isNotEmpty()) { parameters["PentestFindingsDataSource"] =
JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].findings) if (projectReportCollection.projectPentestReport[i].findings.isNotEmpty()) {
} else { JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].findings)
JRBeanCollectionDataSource(emptyList<Finding>()) } else {
} JRBeanCollectionDataSource(emptyList<Finding>())
parameters["PentestCommentsDataSource"] = if (projectReportCollection.projectPentestReport[i].comments.isNotEmpty()) { }
JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].comments) parameters["PentestCommentsDataSource"] =
} else { if (projectReportCollection.projectPentestReport[i].comments.isNotEmpty()) {
JRBeanCollectionDataSource(emptyList<Comment>()) JRBeanCollectionDataSource(projectReportCollection.projectPentestReport[i].comments)
} } else {
JRBeanCollectionDataSource(emptyList<Comment>())
}
// Fill Reports // Fill Reports
// Print one report for each objective and merge them together afterwards // Print one report for each objective and merge them together afterwards
val jasperPrintPentests: JasperPrint = val jasperPrintPentests: JasperPrint =

View File

@ -0,0 +1,5 @@
## IdentityProvider (Keycloak) ##
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://c4po-keycloak:8080/auth/realms/c4po_realm_local
keycloakhost=c4po-keycloak
keycloak.client.url=http://c4po-keycloak:8080
keycloak.client.realm.path=auth/realms/c4po_realm_local/

View File

@ -0,0 +1,4 @@
## IdentityProvider (Keycloak) ##
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local
keycloakhost=localhost
keycloak.client.url=http://localhost:8080/

View File

@ -0,0 +1,3 @@
## IdentityProvider (Keycloak) ##
keycloak.client.url=http://localhost:9999
keycloak.client.realm.path=auth/realms/c4po_realm_local/

View File

@ -20,10 +20,11 @@ api.client.findings.path=pentests/findings
api.client.comments.path=pentests/comments api.client.comments.path=pentests/comments
## IdentityProvider (Keycloak) ## ## IdentityProvider (Keycloak) ##
# spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local
# keycloakhost=localhost external.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local
# keycloak.client.url=http://localhost:8888 keycloakhost=localhost
# keycloak.client.realm.path=auth/realms/c4po_realm_local/ keycloak.client.url=http://localhost:8080
keycloak.client.realm.path=auth/realms/c4po_realm_local/
## Total number of pentests listet in the OWASP testing guide ## Total number of pentests listet in the OWASP testing guide
## https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf ## https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf

File diff suppressed because one or more lines are too long