feat: added mongodb to microservice and docker compose

This commit is contained in:
mhg 2021-08-06 16:27:17 +02:00 committed by Marcel Haag
parent dfc5e1a934
commit 47dd096da6
40 changed files with 4627 additions and 202 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.DS_Store
# Volumes
/security-c4po-cfg/volumes/keycloak/data/*
/security-c4po-cfg/volumes/mongodb/data/*

23
c4po.sh
View File

@ -1,11 +1,17 @@
#!/bin/bash
docker_reg="c4po.io"
baseDir=$(pwd)
composeDir=$baseDir"/security-c4po-cfg"
keycloakVolume="security-c4po-cfg/volumes/keycloak/data/*"
mongoVolume="security-c4po-cfg/volumes/mongodb/data/*"
composeKeycloak=$baseDir"/security-c4po-cfg/kc/docker-compose.keycloak.yml"
composeDatabase=$baseDir"/security-c4po-cfg/mongodb/docker-compose.mongodb.yml"
composeFrontend=$baseDir"/security-c4po-cfg/frontend/docker-compose.frontend.yml"
composeBackend=$baseDir"/security-c4po-cfg/backend/docker-compose.backend.yml"
compose=$baseDir"/security-c4po-cfg/docker-compose.yml"
echo -e "
_______ _______ _______ _ _ ______ _____ _______ __ __
|______ |______ | | | |_____/ | | \_/
@ -18,12 +24,13 @@ ______| |______ |_____ |_____| | \_ __|__ | | _/_/_/ _/
echo "-------------CLEAN UP Container---------------"
echo -e "\n"
#docker rm -f security-c4po-keycloak
#docker rm -f security-c4po-postgres-keycloak
docker rm -f security-c4po-api
docker rm -f security-c4po-angular
rm -r ${keycloakVolume}
docker rm -f c4po-keycloak
docker rm -f c4po-keycloak-postgres
docker rm -f c4po-db
docker rm -f c4po-api
docker rm -f c4po-angular
echo -e "\n"
echo "-----------------Start Build------------------"
echo -e "\n"
echo " - Backend: "
@ -32,7 +39,9 @@ echo -e "\n"
echo " - Frontend: "
docker-compose -f ${composeFrontend} build
echo -e "\n"
# docker-compose -f ${compose} up
echo "------------Start Docker Container------------"
echo -e "\n"
docker-compose -f ${composeKeycloak} -f ${composeBackend} -f ${composeFrontend} up
docker-compose -f ${composeKeycloak} -f ${composeDatabase} -f ${composeBackend} -f ${composeFrontend} up
# docker-compose -f ${compose} up

View File

@ -1,5 +1,6 @@
HELP.md
.gradle
*.DS_Store
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/

View File

@ -1,7 +1,20 @@
FROM openjdk:11-jre
ENV TZ=Europe/Berlin
RUN groupadd -g 9999 security-c4po-api && \
useradd -r -u 9999 -g security-c4po-api security-c4po-api
RUN mkdir /data
RUN chown security-c4po-api:security-c4po-api /data
USER security-c4po-api
# GET CURRENT STAGE
ARG STAGE
ENV ENV_STAGE=$STAGE
# COPY PACKAGE INTO IMAGE
COPY ./build/libs/security-c4po-api-0.0.1-SNAPSHOT.jar .
COPY ./build/libs/security-c4po-api-0.0.1-SNAPSHOT.jar /
USER security-c4po-api
EXPOSE 8443
# RUN JAVA
CMD [ "java", "-jar", "security-c4po-api-0.0.1-SNAPSHOT.jar" ]
# CMD [ "java", "-jar", "security-c4po-api-0.0.1-SNAPSHOT.jar" ]
ENTRYPOINT [ "java", "-jar", "-Dspring.profiles.active=${ENV_STAGE}", "security-c4po-api-0.0.1-SNAPSHOT.jar" ]

View File

@ -36,6 +36,8 @@ repositories {
mavenCentral()
}
apply(plugin = "org.asciidoctor.jvm.convert")
dependencyCheck {
autoUpdate = true
cveValidForHours = 1
@ -59,28 +61,33 @@ val snippetsDir = file("build/generated-snippets")
dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.11.3")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions:1.1.1")
implementation("javax.websocket:javax.websocket-api:1.1")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions:1.1.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
/*implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")*/
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.github.spotbugs:spotbugs-annotations:4.1.2")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation("com.auth0:java-jwt:3.18.1")
implementation("org.modelmapper:modelmapper:2.3.2")
api("org.springframework.boot:spring-boot-starter-test")
api("org.springframework.security:spring-security-jwt:1.1.1.RELEASE")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.3.1")
testImplementation("org.springframework.cloud:spring-cloud-contract-wiremock:2.1.0.RELEASE")
testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient")
testImplementation("com.github.spotbugs:spotbugs-annotations:4.1.2")
testApi("org.testcontainers:junit-jupiter:1.15.2")
}
jacoco {

View File

@ -5,7 +5,11 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
class Appuser : UserDetails {
class Appuser internal constructor(
val sub: String,
val extractedUsername: String,
val token: String
) : UserDetails {
override fun getAuthorities(): Collection<GrantedAuthority> {
return listOf("user").stream().map {

View File

@ -9,19 +9,33 @@ 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 reactor.kotlin.core.publisher.toMono
import java.util.stream.Collectors
/** JWT converter that takes the roles from 'groups' claim of JWT token. */
class AppuserJwtAuthConverter(
private val appuserDetailsService: UserAccountDetailsService) : Converter<Jwt, Mono<AbstractAuthenticationToken>> {
class AppuserJwtAuthConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>> {
override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> {
val authorities = extractAuthorities(jwt)
return appuserDetailsService
.findByUsername(jwt.getClaimAsString("sub"))
.map { u ->
UsernamePasswordAuthenticationToken(u, "n/a", authorities);
}
val sub = extractSub(jwt)
val username = extractUserName(jwt)
return UsernamePasswordAuthenticationToken(Appuser(sub, username, jwt.tokenValue!!), "n/a", authorities).toMono()
}
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> {
@ -35,19 +49,18 @@ class AppuserJwtAuthConverter(
private fun getScopes(jwt: Jwt): Collection<String> {
val mapper = ObjectMapper()
val scopes = jwt.getClaims().get(GROUPS_CLAIM).toString()
if (scopes != null) {
val roleStringValue = mapper.readTree(scopes).get("roles").toString()
val roles = mapper.readValue<Collection<String>>(roleStringValue)
if (!roles.isEmpty()){
return roles
}
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

@ -1,15 +0,0 @@
package com.securityc4po.api.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

@ -1,18 +1,18 @@
package com.securityc4po.api.configuration.security
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Lazy
import org.springframework.http.HttpMethod
import org.springframework.web.cors.CorsConfiguration
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.web.server.SecurityWebFilterChain
import org.springframework.web.cors.CorsConfiguration
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class WebSecurityConfiguration(private val userAccountDetailsService: UserAccountDetailsService) {
class WebSecurityConfiguration {
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
@ -43,6 +43,6 @@ class WebSecurityConfiguration(private val userAccountDetailsService: UserAccoun
@Bean
fun appuserJwtAuthenticationConverter(): AppuserJwtAuthConverter {
return AppuserJwtAuthConverter(userAccountDetailsService)
return AppuserJwtAuthConverter()
}
}

View File

@ -2,35 +2,27 @@ package com.securityc4po.api.project
import com.fasterxml.jackson.annotation.JsonFormat
import com.securityc4po.api.ResponseBody
import org.springframework.data.mongodb.core.index.Indexed
import java.time.Instant
import java.util.UUID
data class Project(
/*
* @Indexed(background = true, unique = true)
* Can be used after adding deps for mongodb
*/
val id: String = UUID.randomUUID().toString(),
val client: String,
val title: String,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
/* Change to Instant after database integration */
val createdAt: String,
val tester: String? = null,
val logo: String? = null
@Indexed(background = true, unique = true)
val id: String = UUID.randomUUID().toString(),
val client: String,
val title: String,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
val createdAt: String = Instant.now().toString(),
val tester: String? = null,
val logo: String? = null
)
fun Project.toProjectResponseBody(): ResponseBody {
return kotlin.collections.mapOf(
return mapOf(
"id" to id,
"client" to client,
"title" to title,
"createdAt" to createdAt.toString(),
"createdAt" to createdAt,
"tester" to tester,
"logo" to logo
)

View File

@ -4,9 +4,9 @@ import com.securityc4po.api.configuration.BC_BAD_CAST_TO_ABSTRACT_COLLECTION
import com.securityc4po.api.extensions.getLoggerFor
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import com.securityc4po.api.ResponseBody
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import reactor.core.publisher.Mono
@RestController
@RequestMapping("/projects")
@ -22,8 +22,13 @@ class ProjectController(private val projectService: ProjectService) {
var logger = getLoggerFor<ProjectController>()
@GetMapping
fun getProjects(): List<Project> {
return projectService.getProjects()
fun getProjects(): Mono<ResponseEntity<List<ResponseBody>>> {
return projectService.getProjects().map {
it.map {
it.toProjectResponseBody()
}
}.map {
ResponseEntity.ok(it)
}
}
}

View File

@ -1,11 +1,26 @@
package com.securityc4po.api.project
import com.securityc4po.api.BaseEntity
import org.springframework.data.mongodb.core.mapping.Document
/*
* @Document(collection = "project")
* Can be used after adding deps for mongodb
*/
@Document(collection = "projects")
open class ProjectEntity(
data: Project
) : BaseEntity<Project>(data)
fun ProjectEntity.toProject() : Project {
return Project(
this.data.id,
this.data.client,
this.data.title,
this.data.createdAt,
this.data.tester,
this.data.logo
)
}
fun List<ProjectEntity>.toProjects(): List<Project> {
return this.map {
it.toProject()
}
}

View File

@ -0,0 +1,13 @@
package com.securityc4po.api.project
import org.springframework.data.mongodb.repository.Query
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.stereotype.Repository
import reactor.core.publisher.Mono
@Repository
interface ProjectRepository: ReactiveMongoRepository<ProjectEntity, String> {
@Query("{'data._id' : ?0}")
fun findProjectById(id: String): Mono<ProjectEntity>
}

View File

@ -1,39 +1,22 @@
package com.securityc4po.api.project
import com.securityc4po.api.extensions.getLoggerFor
import org.junit.BeforeClass
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
/* Remove after database is integrated */
import java.io.File
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import reactor.core.publisher.Mono
@Service
class ProjectService() {
class ProjectService(private val projectRepository: ProjectRepository) {
var logger = getLoggerFor<ProjectService>()
/* Remove after database is integrated */
val mapper = jacksonObjectMapper()
@BeforeClass
fun init() {
mapper.registerKotlinModule()
mapper.registerModule(JavaTimeModule())
}
/**
* Get all [Project]s
*
* @return list of [Project]
*/
fun getProjects(): List<Project> {
val jsonProjectsString: String = File("./src/main/resources/mocks/projects.json").readText(Charsets.UTF_8)
val jsonProjectList: List<Project> = mapper.readValue<List<Project>>(jsonProjectsString)
/* After database integration the return should be Flux of ProjectEntity */
return jsonProjectList;
fun getProjects(): Mono<List<Project>> {
return projectRepository.findAll().collectList().map {
it.map { projectEntity -> projectEntity.toProject() }
}
}
}

View File

@ -0,0 +1,9 @@
## IdentityProvider (Keycloak) ##
# spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local
# keycloakhost=localhost
# keycloak.client.url=http://localhost:8888/
## Database (MONGODB) Config ##
#spring.data.mongodb.host=c4po-db
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

View File

@ -0,0 +1,8 @@
## IdentityProvider (Keycloak) ##
# spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local
# keycloakhost=localhost
# keycloak.client.url=http://localhost:8888/
## Database (MONGODB) Config ##
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

View File

@ -1,6 +1,7 @@
## General Properties ##
spring.main.web-application-type=reactive
# spring.main.web-application-type=reactive
spring.main.allow-bean-definition-overriding=true
spring.jackson.default-property-inclusion=non_null
## Server Config ##
server.port=8443
@ -11,13 +12,12 @@ management.endpoint.health.enabled=true
management.endpoints.web.exposure.include=info, health, metrics
## Database (MONGODB) Config ##
# spring.data.mongodb.database=C4PO
# spring.data.mongodb.host=localhost
# spring.data.mongodb.port=27017
# spring.main.allow-bean-definition-overriding=true
# spring.data.mongodb.auto-index-creation=true
spring.data.mongodb.database=c4po
spring.data.mongodb.auto-index-creation=true
## IdentityProvider (Keycloak for tests) ##
## IdentityProvider (Keycloak) ##
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local
keycloakhost=localhost
keycloak.client.url=http://localhost:8888/
keycloak.client.url=http://localhost:8888/
# keycloak.client.realm.path=auth/realms/c4po_realm_local/
idp.jwt.claim.name.user=username

View File

@ -1,9 +1,105 @@
package com.securityc4po.api
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.github.dockerjava.api.model.ExposedPort
import com.github.dockerjava.api.model.PortBinding
import com.github.dockerjava.api.model.Ports
import com.nimbusds.jwt.JWTParser
import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Value
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock
import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders
import org.springframework.http.MediaType
import org.springframework.test.context.TestPropertySource
import org.springframework.util.LinkedMultiValueMap
import org.springframework.web.client.RestTemplate
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
import org.testcontainers.containers.GenericContainer
import org.testcontainers.images.builder.ImageFromDockerfile
import org.testcontainers.utility.DockerImageName
import org.testcontainers.utility.MountableFile
import java.nio.file.Paths
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@AutoConfigureWireMock(port = 0)
@TestPropertySource(properties = [
"spring.data.mongodb.port=10002",
"spring.data.mongodb.authentication-database=admin",
"spring.data.mongodb.password=test",
"spring.data.mongodb.username=testuser",
"MONGO_DB_MAX_CONNECTION_IDLE_TIME=PT25M",
"DATA_REFRESH_THRESHOLD_DURATION=PT30M",
"CLEANUP_BATCH_SIZE_FOR_SELECTING_EXPIRED_USERS=100"
])
abstract class BaseContainerizedTest {
@Value("\${keycloakhost}")
var keycloakhost: String? = null
companion object {
val mongoDbContainer = KGenericContainer(ImageFromDockerfile("c4poapibasecontainerizedtest").withDockerfileFromBuilder {
it.from("mongo")
it.env("MONGO_INITDB_ROOT_USERNAME", "root")
it.env("MONGO_INITDB_ROOT_PASSWORD", "cjwkbencowepoc324pon2mop3mp4")
it.env("MONGO_INITDB_DATABASE", "admin")
it.add("insert-mongodb-user.js", "/docker-entrypoint-initdb.d")
}.withFileFromPath("insert-mongodb-user.js", Paths.get(MountableFile.forClasspathResource("insert-mongodb-user.js", 700).resolvedPath))
).apply {
withCreateContainerCmdModifier {
it.hostConfig?.withPortBindings(PortBinding(Ports.Binding.bindPort(10002), ExposedPort(27017)))
}
start()
}
val keycloakContainer = KGenericContainerFromImage(DockerImageName.parse("jboss/keycloak:6.0.1")).apply {
withEnv("KEYCLOAK_USER", "admin")
withEnv("KEYCLOAK_PASSWORD", "admin")
withEnv("KEYCLOAK_IMPORT", "/tmp/realm.json")
withEnv("DB_VENDOR", "h2")
withCreateContainerCmdModifier {
it.hostConfig?.withPortBindings(PortBinding(Ports.Binding.bindPort(8888), ExposedPort(8080)))
}
withCopyFileToContainer(MountableFile.forClasspathResource("outdated_realm-export.json", 700), "/tmp/realm.json")
withCopyFileToContainer(MountableFile.forClasspathResource("create-keycloak-user.sh", 700),
"/opt/jboss/create-keycloak-user.sh")
start()
println("== Inserting users must wait until Keycloak is started completely ==")
execInContainer("sh", "/opt/jboss/create-keycloak-user.sh")
}
}
var token = "n/a"
var tokenAdmin = "n/a"
var tokenUser = "n/a"
var keycloakHost: String? = null
fun getAccessToken(username: String, password: String, clientId: String, realm: String): String {
keycloakHost = "http://" + keycloakhost + ":" + keycloakContainer.getMappedPort(8080)
val restTemplate = RestTemplate()
val headers = HttpHeaders()
headers.contentType = MediaType.APPLICATION_FORM_URLENCODED
val map = LinkedMultiValueMap<Any, Any>()
map.add("grant_type", "password")
map.add("client_id", clientId)
map.add("username", username)
map.add("password", password)
map.add("client_secret", "secret")
val responseString = restTemplate.postForObject("$keycloakHost/auth/realms/$realm/protocol/openid-connect/token",
HttpEntity<Any>(map, headers), String::class.java)
val token = ObjectMapper().readValue(responseString, KeyCloakToken::class.java)
return token.access_token!!
}
fun getSubClaim(token: String): String {
val jwt = JWTParser.parse(token)
val scopes = ObjectMapper().readValue(jwt.jwtClaimsSet.toJSONObject().toJSONString(), HashMap::class.java)
return scopes["sub"] as String
}
@JsonIgnoreProperties(ignoreUnknown = true)
class KeyCloakToken(val access_token: String? = null)
class KGenericContainerFromImage(imageName: DockerImageName) : GenericContainer<KGenericContainerFromImage>(imageName)
class KGenericContainer(dockerFile: ImageFromDockerfile) : GenericContainer<KGenericContainer>(dockerFile)
}

View File

@ -28,7 +28,7 @@ abstract class BaseDocumentationIntTest : BaseContainerizedTest() {
@BeforeEach
fun setupDocs(restDocumentation: RestDocumentationContextProvider) {
webTestClient = WebTestClient.bindToServer()
.baseUrl("com.securityc4po.api.http://localhost:$port")
.baseUrl("http://localhost:$port")
.filter(documentationConfiguration(restDocumentation))
.responseTimeout(Duration.ofMillis(10000))
.build()

View File

@ -8,4 +8,4 @@ import org.springframework.test.context.junit.jupiter.SpringExtension
@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext
abstract class BaseIntTest : BaseContainerizedTest() { }
abstract class BaseIntTest : BaseContainerizedTest()

View File

@ -9,6 +9,7 @@ import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.restdocs.operation.preprocess.Preprocessors
import org.springframework.restdocs.payload.JsonFieldType
import org.springframework.restdocs.payload.PayloadDocumentation
@ -18,8 +19,8 @@ import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
@SuppressFBWarnings(SIC_INNER_SHOULD_BE_STATIC)
class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
/*@Autowired
lateinit var mongoTemplate: MongoTemplate*/
@Autowired
lateinit var mongoTemplate: MongoTemplate
@BeforeEach
fun init() {

View File

@ -5,38 +5,54 @@ import com.securityc4po.api.BaseIntTest
import com.securityc4po.api.configuration.SIC_INNER_SHOULD_BE_STATIC
import com.securityc4po.api.configuration.URF_UNREAD_FIELD
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import io.netty.handler.ssl.SslContextBuilder
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.web.server.LocalServerPort
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.query.Query
import org.springframework.test.context.TestPropertySource
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.util.ResourceUtils
import reactor.netty.http.client.HttpClient
import java.time.Duration
@AutoConfigureWireMock(port = 0)
@SuppressFBWarnings(SIC_INNER_SHOULD_BE_STATIC, URF_UNREAD_FIELD, "Unread field will become used after database implementation")
/*@TestPropertySource(
properties = [
"keycloak.client.url=http://localhost:${'$'}{wiremock.server.port}"
]
)*/
@SuppressFBWarnings(
SIC_INNER_SHOULD_BE_STATIC,
URF_UNREAD_FIELD,
"Unread field will become used after database implementation"
)
class ProjectControllerIntTest : BaseIntTest() {
@LocalServerPort
private var port = 0
@Autowired
lateinit var mongoTemplate: MongoTemplate
private lateinit var webTestClient: WebTestClient
@BeforeEach
fun setupWebClient() {
webTestClient = WebTestClient.bindToServer()
.baseUrl("http://localhost:$port")
.responseTimeout(Duration.ofMillis(10000))
.build()
.baseUrl("http://localhost:$port")
.responseTimeout(Duration.ofMillis(10000))
.build()
}
/*@Autowired
lateinit var mongoTemplate: MongoTemplate*/
@BeforeEach
fun init() {
cleanUp()
configureAdminToken()
persistBasicTestScenario()
}
@ -44,63 +60,67 @@ class ProjectControllerIntTest : BaseIntTest() {
inner class GetProjects {
@Test
fun `requesting projects successfully`() {
/* Implement after the implementation of database */
/*webTestClient.get().uri("/v1/projects")
.header("")
.exchange()
.expectStatus().isOk
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(getProjects()))*/
webTestClient.get().uri("/v1/projects")
.header("Authorization", "Bearer $tokenAdmin")
.exchange()
.expectStatus().isOk
.expectHeader().valueEquals("Application-Name", "security-c4po-api")
.expectBody().json(Json.write(getProjects()))
}
val projectOne = Project(
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
client = "E Corp",
title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester",
logo = "Insert'E_Corp.png'BASE64Encoded"
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
client = "E Corp",
title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester",
logo = "Insert'E_Corp.png'BASE64Encoded"
)
val projectTwo = Project(
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
client = "Allsafe",
title = "CashMyData (iOS)",
createdAt = "2021-01-10T18:05:00Z",
tester = "Elliot",
logo = "Insert'Allsafe.png'BASE64Encoded"
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
client = "Allsafe",
title = "CashMyData (iOS)",
createdAt = "2021-01-10T18:05:00Z",
tester = "Elliot",
logo = "Insert'Allsafe.png'BASE64Encoded"
)
private fun getProjects() = listOf(
projectOne.toProjectResponseBody(),
projectTwo.toProjectResponseBody()
projectOne.toProjectResponseBody(),
projectTwo.toProjectResponseBody()
)
}
private fun cleanUp() {
/*mongoTemplate.findAllAndRemove(Query(), Project::class.java)*/
mongoTemplate.findAllAndRemove(Query(), Project::class.java)
tokenAdmin = "n/a"
}
private fun persistBasicTestScenario() {
// setup test data
val projectOne = Project(
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
client = "E Corp",
title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester",
logo = "Insert'E_Corp.png'BASE64Encoded"
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
client = "E Corp",
title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester",
logo = "Insert'E_Corp.png'BASE64Encoded"
)
val projectTwo = Project(
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
client = "Allsafe",
title = "CashMyData (iOS)",
createdAt = "2021-01-10T18:05:00Z",
tester = "Elliot",
logo = "Insert'Allsafe.png'BASE64Encoded"
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
client = "Allsafe",
title = "CashMyData (iOS)",
createdAt = "2021-01-10T18:05:00Z",
tester = "Elliot",
logo = "Insert'Allsafe.png'BASE64Encoded"
)
cleanUp()
/*mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(ProjectEntity(projectTwo))*/
mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(ProjectEntity(projectTwo))
}
private fun configureAdminToken() {
tokenAdmin = getAccessToken("test_admin", "test", "c4po_local", "c4po_realm_local")
}
}

View File

@ -12,7 +12,9 @@ class ProjectServiceTest {
private val log = mock<Logger>()
private val cut = ProjectService().apply {
private val projectRepository = mock<ProjectRepository>()
private val cut = ProjectService(projectRepository).apply {
this.logger = log
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
cd keycloak/bin
sleep 20
./kcadm.sh config credentials --server http://localhost:8080/auth --realm master --user admin --password admin
USERID=$(./kcadm.sh create users -r c4po_realm_local -s username=test_admin \
-s email=Test.Admin@heros.com \
-s firstName=test \
-s lastName=admin \
-s attributes.lang="de-DE" \
-s attributes.datenumberformat="en-US" \
-o --fields id | jq '.id' | tr -d '"')
./kcadm.sh update users/$USERID/reset-password -r c4po_realm_test -s type=password -s value=test -s temporary=false -n
./kcadm.sh add-roles --uusername test_admin --rolename c4po_admin -r c4po_realm_test
./kcadm.sh add-roles -r c4po_realm_test --uusername test_admin --cclientid realm-management --rolename create-client --rolename view-users
USERID=$(./kcadm.sh create users -r c4po_realm_local -s username=test_user \
-s email=Test.User@heros.com \
-s firstName=test \
-s lastName=user \
-s attributes.lang="de-DE" \
-s attributes.datenumberformat="en-US" \
-o --fields id | jq '.id' | tr -d '"')
./kcadm.sh update users/$USERID/reset-password -r c4po_realm_test -s type=password -s value=test -s temporary=false -n
./kcadm.sh add-roles --uusername test_user --rolename c4po_user -r c4po_realm_test
./kcadm.sh add-roles -r c4po_realm_test --uusername test_user --cclientid realm-management --rolename create-client --rolename view-users

View File

@ -0,0 +1,12 @@
db.createUser(
{
user: "testuser",
pwd: "test",
roles: [
{
role: "readWrite",
db: "c4po"
}
]
}
);

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
cd keycloak/bin
sleep 20
./kcadm.sh config credentials --server http://localhost:8888/auth --realm master --user admin --password admin
USERID=$(./kcadm.sh create users -r c4po_realm_local -s username=test_admin \
-s email=Troy.Stewart@heros.com \
-s firstName=test \
-s lastName=admin \
-s attributes.lang="de-DE" \
-s attributes.datenumberformat="en-US" \
-o --fields id | jq '.id' | tr -d '"')
./kcadm.sh update users/$USERID/reset-password -r c4po_realm_test -s type=password -s value=test -s temporary=false -n
./kcadm.sh add-roles --uusername test_admin --rolename c4po_admin -r c4po_realm_test
./kcadm.sh add-roles -r c4po_realm_test --uusername test_admin --cclientid realm-management --rolename create-client --rolename view-users
USERID=$(./kcadm.sh create users -r c4po_realm_local -s username=test_user \
-s email=Troy.Stewart@heros.com \
-s firstName=test \
-s lastName=user \
-s attributes.lang="de-DE" \
-s attributes.datenumberformat="en-US" \
-o --fields id | jq '.id' | tr -d '"')
./kcadm.sh update users/$USERID/reset-password -r c4po_realm_test -s type=password -s value=test -s temporary=false -n
./kcadm.sh add-roles --uusername test_user --rolename c4po_user -r c4po_realm_test
./kcadm.sh add-roles -r c4po_realm_test --uusername test_user --cclientid realm-management --rolename create-client --rolename view-users

View File

@ -4,13 +4,18 @@ services:
c4po-api:
build: '../../security-c4po-api'
image: security-c4po-api:latest
container_name: security-c4po-api
container_name: c4po-api
environment:
- SPRING_PROFILES_ACTIVE=COMPOSE
depends_on:
- c4po-db
- c4po-keycloak
links:
- c4po-db
- c4po-keycloak
deploy:
resources:
limits:
memory: "1G"
ports:
- '8443:8443'
networks:
c4po:
- 8443:8443

View File

@ -1,6 +1,8 @@
{
"id" : "c4po_realm_local",
"realm" : "c4po_realm_local",
"displayName" : "C4PO",
"displayNameHtml" : "<div><span>C4PO</span></div>",
"notBefore" : 0,
"revokeRefreshToken" : false,
"refreshTokenMaxReuse" : 0,
@ -380,7 +382,7 @@
} ],
"disableableCredentialTypes" : [ ],
"requiredActions" : [ ],
"realmRoles" : [ "uma_authorization", "c4po_user", "offline_access", "c4po_admin" ],
"realmRoles" : [ "uma_authorization", "c4po_user", "c4po_admin" ],
"clientRoles" : {
"c4po_local" : [ "user" ],
"account" : [ "view-profile", "manage-account" ]
@ -659,8 +661,8 @@
"protocol" : "openid-connect",
"attributes" : {
"saml.assertion.signature" : "false",
"saml.multivalued.roles" : "false",
"saml.force.post.binding" : "false",
"saml.multivalued.roles" : "false",
"saml.encrypt" : "false",
"saml.server.signature" : "false",
"saml.server.signature.keyinfo.ext" : "false",
@ -680,7 +682,7 @@
}, {
"id" : "8badc11a-50e4-44ae-a292-47e3759fcaeb",
"clientId" : "security-c4po-api",
"rootUrl" : "http://localhost:8443",
"rootUrl" : "",
"adminUrl" : "",
"baseUrl" : "http://localhost:8443/",
"surrogateAuthRequired" : false,
@ -702,8 +704,8 @@
"protocol" : "openid-connect",
"attributes" : {
"saml.assertion.signature" : "false",
"saml.multivalued.roles" : "false",
"saml.force.post.binding" : "false",
"saml.multivalued.roles" : "false",
"saml.encrypt" : "false",
"saml.server.signature" : "false",
"saml.server.signature.keyinfo.ext" : "false",
@ -1206,7 +1208,7 @@
"subType" : "anonymous",
"subComponents" : { },
"config" : {
"allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "saml-role-list-mapper" ]
"allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-property-mapper" ]
}
}, {
"id" : "cc2d0cd7-3d3f-4b0a-ad95-7118f36bf188",
@ -1238,7 +1240,7 @@
"subType" : "authenticated",
"subComponents" : { },
"config" : {
"allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper" ]
"allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ]
}
}, {
"id" : "92230e65-7480-44c3-af2d-72ddee758cbc",
@ -1287,7 +1289,7 @@
"internationalizationEnabled" : false,
"supportedLocales" : [ ],
"authenticationFlows" : [ {
"id" : "2c54f19d-5992-447d-a2b3-58953c5a92d9",
"id" : "fa5fc78f-19a9-4737-868b-618163f28c79",
"alias" : "Account verification options",
"description" : "Method with which to verity the existing account",
"providerId" : "basic-flow",
@ -1307,7 +1309,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "58e457d2-c138-401b-94e6-aa0c89d40be5",
"id" : "01735b0f-139f-46e5-bb63-f797a27efa77",
"alias" : "Authentication Options",
"description" : "Authentication options.",
"providerId" : "basic-flow",
@ -1333,7 +1335,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "ed8a0f0d-c571-4c7c-8177-51ff71e0cb0e",
"id" : "a7666cf0-626c-48c4-9e71-e408832de725",
"alias" : "Browser - Conditional OTP",
"description" : "Flow to determine if the OTP is required for the authentication",
"providerId" : "basic-flow",
@ -1353,7 +1355,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "b12f09d6-8e47-4fa2-80cb-6adece38f970",
"id" : "1dfabb7a-efdd-4964-bba5-389cad79b654",
"alias" : "Direct Grant - Conditional OTP",
"description" : "Flow to determine if the OTP is required for the authentication",
"providerId" : "basic-flow",
@ -1373,7 +1375,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "571ebbe3-1dbd-4c11-a048-431ebe7b9ba0",
"id" : "c3b2bf2b-3da8-430d-a9b7-8793c3dc30a3",
"alias" : "First broker login - Conditional OTP",
"description" : "Flow to determine if the OTP is required for the authentication",
"providerId" : "basic-flow",
@ -1393,7 +1395,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "9efe3906-9789-45d5-bd69-17657e7d0dd1",
"id" : "44343bdf-8592-4242-835f-e349943a110b",
"alias" : "Handle Existing Account",
"description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider",
"providerId" : "basic-flow",
@ -1413,7 +1415,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "ff2fc973-bd97-49f6-a5b9-234904131b12",
"id" : "e72b8fcb-cd8b-4e7a-a057-3446b806b538",
"alias" : "Reset - Conditional OTP",
"description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
"providerId" : "basic-flow",
@ -1433,7 +1435,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "b4b2f650-4ae7-4d26-ae58-59e6074fb067",
"id" : "2416145b-4d20-493c-bdf7-419898c002ee",
"alias" : "User creation or linking",
"description" : "Flow for the existing/non-existing user alternatives",
"providerId" : "basic-flow",
@ -1454,7 +1456,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "5848478b-6cb0-4460-a0be-9393e8835382",
"id" : "b7ff8aad-2daa-4736-8815-f3e8f0df391e",
"alias" : "Verify Existing Account by Re-authentication",
"description" : "Reauthentication of existing account",
"providerId" : "basic-flow",
@ -1474,7 +1476,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "79af744f-037e-49b7-b469-4581078db93a",
"id" : "8339d3ba-2d0a-4d23-bbfa-a78e4973d3c9",
"alias" : "browser",
"description" : "browser based authentication",
"providerId" : "basic-flow",
@ -1506,7 +1508,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "bed9a42e-29ce-42e3-b217-4c82ddc1da60",
"id" : "5ece002a-4e62-4d0d-8705-4b116164b424",
"alias" : "clients",
"description" : "Base authentication for clients",
"providerId" : "client-flow",
@ -1538,7 +1540,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "90ed59d7-616e-4db0-b5b7-b02c4778bfe6",
"id" : "bd27b0dc-bc87-40b7-a626-491b9955668d",
"alias" : "direct grant",
"description" : "OpenID Connect Resource Owner Grant",
"providerId" : "basic-flow",
@ -1564,7 +1566,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "02136f0a-354a-41d6-8a81-82d9d61f8ae1",
"id" : "2db79d60-7c9d-4516-80f0-0c5d60349899",
"alias" : "docker auth",
"description" : "Used by Docker clients to authenticate against the IDP",
"providerId" : "basic-flow",
@ -1578,7 +1580,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "61049b62-3f5e-440e-b3d3-7955c74ce79a",
"id" : "25a92fbe-7d4d-46bc-a751-29ef844290a3",
"alias" : "first broker login",
"description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
"providerId" : "basic-flow",
@ -1599,7 +1601,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "a3416906-b38a-4626-9759-99010c6e27b9",
"id" : "26f6a5db-9be8-446c-82d0-6f4e29b5f08d",
"alias" : "forms",
"description" : "Username, password, otp and other auth forms.",
"providerId" : "basic-flow",
@ -1619,7 +1621,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "e53c6c19-574d-4ba0-b8b5-55631d71328d",
"id" : "05a94701-ad98-4bbc-a162-746a107afba5",
"alias" : "http challenge",
"description" : "An authentication flow based on challenge-response HTTP Authentication Schemes",
"providerId" : "basic-flow",
@ -1639,7 +1641,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "3bc89297-8fa2-45a0-a4f9-64166c5f53f2",
"id" : "75347884-d4cb-4eba-9b89-63566d509b92",
"alias" : "registration",
"description" : "registration flow",
"providerId" : "basic-flow",
@ -1654,7 +1656,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "7f080e51-3567-498e-ae34-1abeefe07495",
"id" : "74e3a2d3-ecda-400d-8bff-0926dc272e4b",
"alias" : "registration form",
"description" : "registration form",
"providerId" : "form-flow",
@ -1686,7 +1688,7 @@
"autheticatorFlow" : false
} ]
}, {
"id" : "d0fe2454-5b33-47bd-9909-fd81dc62e27b",
"id" : "6eae8652-baf7-4a7d-80a4-1711906caec7",
"alias" : "reset credentials",
"description" : "Reset credentials for a user if they forgot their password or something",
"providerId" : "basic-flow",
@ -1718,7 +1720,7 @@
"autheticatorFlow" : true
} ]
}, {
"id" : "f41e0e59-1927-4f6b-9917-1fffc76ed300",
"id" : "6135710b-b019-4117-ba32-578d3d496b2a",
"alias" : "saml ecp",
"description" : "SAML ECP Profile Authentication Flow",
"providerId" : "basic-flow",
@ -1733,13 +1735,13 @@
} ]
} ],
"authenticatorConfig" : [ {
"id" : "7328eecb-235e-4d51-aeb8-60f040bdce55",
"id" : "3d3735a0-1362-4f0d-9306-bfc727da1b5b",
"alias" : "create unique user config",
"config" : {
"require.password.update.after.registration" : "false"
}
}, {
"id" : "3ca8532e-719d-420c-9c96-684c1195ece6",
"id" : "c1f4a15f-8234-4f0f-affa-baf610b001e1",
"alias" : "review profile config",
"config" : {
"update.profile.on.first.login" : "missing"

View File

@ -1,6 +1,6 @@
# cfg for local keycloak
DB_VENDOR=postgres
DB_ADDR=keycloak-postgres
DB_ADDR=c4po-keycloak-postgress
DB_PORT=5432
DB_USER=c4po_kc_local
DB_PASSWORD=Test1234!

View File

@ -0,0 +1,89 @@
version: '2'
volumes:
c4po-keycloak-postgres:
c4po-db:
services:
# Database
c4po-keycloak-postgres:
container_name: c4po-keycloak-postgres
image: postgres:latest
env_file:
- cfg/keycloakdb.env
ports:
- 5433:5432
volumes:
- /volumes/keycloak/data/:/var/lib/postgres/data
networks:
- c4po
c4po-db:
image: mongo:latest
container_name: c4po-db
volumes:
- /volumes/mongodb/data/:/db/data
deploy:
resources:
limits:
memory: "1G"
ports:
- 27017:27017
networks:
- c4po
# Authentity Provider
c4po-keycloak:
container_name: c4po-keycloak
depends_on:
- c4po-keycloak-postgres
links:
- c4po-keycloak-postgres
image: jboss/keycloak:11.0.3
volumes:
- /cfg/c4po_realm_export.json/:/tmp/c4po_realm_export.json
ports:
- 8888:8080
env_file:
- cfg/keycloak.env
networks:
- c4po
# Services
c4po-angular:
build: '../security-c4po-angular'
image: security-c4po-angular:latest
container_name: c4po-angular
depends_on:
- c4po-keycloak
links:
- c4po-keycloak
deploy:
resources:
limits:
memory: "1G"
ports:
- 4200:4200
networks:
- c4po
c4po-api:
build: '../security-c4po-api'
image: security-c4po-api:latest
container_name: c4po-api
environment:
- SPRING_PROFILES_ACTIVE=COMPOSE
depends_on:
- c4po-db
- c4po-keycloak
links:
- c4po-db
- c4po-keycloak
deploy:
resources:
limits:
memory: "1G"
ports:
- 8443:8443
networks:
- c4po
networks:
c4po:

View File

@ -4,13 +4,12 @@ services:
c4po-angular:
build: '../../security-c4po-angular'
image: security-c4po-angular:latest
container_name: security-c4po-angular
container_name: c4po-angular
depends_on:
- c4po-keycloak
deploy:
resources:
limits:
memory: "1G"
ports:
- '4200:4200'
networks:
c4po:
- 4200:4200

View File

@ -2,23 +2,22 @@ version: '3.1'
services:
c4po-keycloak:
container_name: security-c4po-keycloak
container_name: c4po-keycloak
depends_on:
- keycloak-postgres
- c4po-keycloak-postgres
image: jboss/keycloak:11.0.3
volumes:
- ../cfg/c4po_realm_export.json:/tmp/c4po_realm_export.json
ports:
- 8888:8080
- 9990:9990
env_file:
- ../cfg/keycloak.env
keycloak-postgres:
container_name: security-c4po-postgres-keycloak
c4po-keycloak-postgress:
container_name: c4po-keycloak-postgres
image: postgres:latest
env_file:
- ../cfg/keycloakdb.env
ports:
- 5433:5432
volumes:
- ../volumes/keycloak/data:/var/lib/postgresql/data
- ../volumes/keycloak/data:/var/lib/postgres/data

View File

@ -0,0 +1,14 @@
version: '3.1'
services:
c4po-db:
image: mongo:latest
container_name: c4po-db
volumes:
- ../volumes/mongodb/data:/data/db
deploy:
resources:
limits:
memory: "1G"
ports:
- 27017:27017