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 #!/bin/bash
docker_reg="c4po.io"
baseDir=$(pwd) 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" 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" composeFrontend=$baseDir"/security-c4po-cfg/frontend/docker-compose.frontend.yml"
composeBackend=$baseDir"/security-c4po-cfg/backend/docker-compose.backend.yml" composeBackend=$baseDir"/security-c4po-cfg/backend/docker-compose.backend.yml"
compose=$baseDir"/security-c4po-cfg/docker-compose.yml"
echo -e " echo -e "
_______ _______ _______ _ _ ______ _____ _______ __ __ _______ _______ _______ _ _ ______ _____ _______ __ __
|______ |______ | | | |_____/ | | \_/ |______ |______ | | | |_____/ | | \_/
@ -18,12 +24,13 @@ ______| |______ |_____ |_____| | \_ __|__ | | _/_/_/ _/
echo "-------------CLEAN UP Container---------------" echo "-------------CLEAN UP Container---------------"
echo -e "\n" echo -e "\n"
#docker rm -f security-c4po-keycloak rm -r ${keycloakVolume}
#docker rm -f security-c4po-postgres-keycloak docker rm -f c4po-keycloak
docker rm -f security-c4po-api docker rm -f c4po-keycloak-postgres
docker rm -f security-c4po-angular docker rm -f c4po-db
docker rm -f c4po-api
docker rm -f c4po-angular
echo -e "\n" echo -e "\n"
echo "-----------------Start Build------------------" echo "-----------------Start Build------------------"
echo -e "\n" echo -e "\n"
echo " - Backend: " echo " - Backend: "
@ -32,7 +39,9 @@ echo -e "\n"
echo " - Frontend: " echo " - Frontend: "
docker-compose -f ${composeFrontend} build docker-compose -f ${composeFrontend} build
echo -e "\n" echo -e "\n"
# docker-compose -f ${compose} up
echo "------------Start Docker Container------------" echo "------------Start Docker Container------------"
echo -e "\n" 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 HELP.md
.gradle .gradle
*.DS_Store
build/ build/
!gradle/wrapper/gradle-wrapper.jar !gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/ !**/src/main/**/build/

View File

@ -1,7 +1,20 @@
FROM openjdk:11-jre 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 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 # 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() mavenCentral()
} }
apply(plugin = "org.asciidoctor.jvm.convert")
dependencyCheck { dependencyCheck {
autoUpdate = true autoUpdate = true
cveValidForHours = 1 cveValidForHours = 1
@ -59,28 +61,33 @@ val snippetsDir = file("build/generated-snippets")
dependencies { dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.11.3") implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:2.11.3")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions:1.1.1") implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("javax.websocket:javax.websocket-api:1.1") implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
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("io.projectreactor.kotlin:reactor-kotlin-extensions:1.1.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 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-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.github.spotbugs:spotbugs-annotations:4.1.2") 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") 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") 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("com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0")
testImplementation("io.projectreactor:reactor-test") testImplementation("io.projectreactor:reactor-test")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1") testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
testImplementation("org.junit.jupiter:junit-jupiter-engine: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.cloud:spring-cloud-contract-wiremock:2.1.0.RELEASE")
testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient") 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 { 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.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails 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> { override fun getAuthorities(): Collection<GrantedAuthority> {
return listOf("user").stream().map { 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.core.authority.SimpleGrantedAuthority
import org.springframework.security.oauth2.jwt.Jwt import org.springframework.security.oauth2.jwt.Jwt
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono
import java.util.stream.Collectors import java.util.stream.Collectors
/** JWT converter that takes the roles from 'groups' claim of JWT token. */ /** JWT converter that takes the roles from 'groups' claim of JWT token. */
class AppuserJwtAuthConverter( class AppuserJwtAuthConverter : Converter<Jwt, Mono<AbstractAuthenticationToken>> {
private val appuserDetailsService: UserAccountDetailsService) : Converter<Jwt, Mono<AbstractAuthenticationToken>> {
override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> { override fun convert(jwt: Jwt): Mono<AbstractAuthenticationToken> {
val authorities = extractAuthorities(jwt) val authorities = extractAuthorities(jwt)
return appuserDetailsService val sub = extractSub(jwt)
.findByUsername(jwt.getClaimAsString("sub")) val username = extractUserName(jwt)
.map { u -> return UsernamePasswordAuthenticationToken(Appuser(sub, username, jwt.tokenValue!!), "n/a", authorities).toMono()
UsernamePasswordAuthenticationToken(u, "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> { private fun extractAuthorities(jwt: Jwt): Collection<GrantedAuthority> {
@ -35,19 +49,18 @@ class AppuserJwtAuthConverter(
private fun getScopes(jwt: Jwt): Collection<String> { private fun getScopes(jwt: Jwt): Collection<String> {
val mapper = ObjectMapper() val mapper = ObjectMapper()
val scopes = jwt.getClaims().get(GROUPS_CLAIM).toString() val scopes = jwt.getClaims().get(GROUPS_CLAIM).toString()
if (scopes != null) { val roleStringValue = mapper.readTree(scopes).get("roles").toString()
val roleStringValue = mapper.readTree(scopes).get("roles").toString() val roles = mapper.readValue<Collection<String>>(roleStringValue)
val roles = mapper.readValue<Collection<String>>(roleStringValue) if (!roles.isEmpty()){
if (!roles.isEmpty()){ return roles
return roles
}
} }
return emptyList() return emptyList()
} }
companion object { companion object {
private val GROUPS_CLAIM = "realm_access" private val GROUPS_CLAIM = "realm_access"
private val ROLE_PREFIX = "ROLE_" 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 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.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Lazy
import org.springframework.http.HttpMethod 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.method.configuration.EnableReactiveMethodSecurity
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.ServerHttpSecurity import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.web.cors.CorsConfiguration
@Configuration
@EnableWebFluxSecurity @EnableWebFluxSecurity
@EnableReactiveMethodSecurity @EnableReactiveMethodSecurity
class WebSecurityConfiguration(private val userAccountDetailsService: UserAccountDetailsService) { class WebSecurityConfiguration {
@Bean @Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
@ -43,6 +43,6 @@ class WebSecurityConfiguration(private val userAccountDetailsService: UserAccoun
@Bean @Bean
fun appuserJwtAuthenticationConverter(): AppuserJwtAuthConverter { 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.fasterxml.jackson.annotation.JsonFormat
import com.securityc4po.api.ResponseBody import com.securityc4po.api.ResponseBody
import org.springframework.data.mongodb.core.index.Indexed
import java.time.Instant import java.time.Instant
import java.util.UUID import java.util.UUID
data class Project( data class Project(
/* @Indexed(background = true, unique = true)
* @Indexed(background = true, unique = true) val id: String = UUID.randomUUID().toString(),
* Can be used after adding deps for mongodb val client: String,
*/ val title: String,
val id: String = UUID.randomUUID().toString(), @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")
val createdAt: String = Instant.now().toString(),
val client: String, val tester: String? = null,
val logo: String? = null
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
) )
fun Project.toProjectResponseBody(): ResponseBody { fun Project.toProjectResponseBody(): ResponseBody {
return kotlin.collections.mapOf( return mapOf(
"id" to id, "id" to id,
"client" to client, "client" to client,
"title" to title, "title" to title,
"createdAt" to createdAt.toString(), "createdAt" to createdAt,
"tester" to tester, "tester" to tester,
"logo" to logo "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 com.securityc4po.api.extensions.getLoggerFor
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import com.securityc4po.api.ResponseBody import com.securityc4po.api.ResponseBody
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.* import org.springframework.web.bind.annotation.*
import reactor.core.publisher.Mono
@RestController @RestController
@RequestMapping("/projects") @RequestMapping("/projects")
@ -22,8 +22,13 @@ class ProjectController(private val projectService: ProjectService) {
var logger = getLoggerFor<ProjectController>() var logger = getLoggerFor<ProjectController>()
@GetMapping @GetMapping
fun getProjects(): List<Project> { fun getProjects(): Mono<ResponseEntity<List<ResponseBody>>> {
return projectService.getProjects() return projectService.getProjects().map {
it.map {
it.toProjectResponseBody()
}
}.map {
ResponseEntity.ok(it)
}
} }
} }

View File

@ -1,11 +1,26 @@
package com.securityc4po.api.project package com.securityc4po.api.project
import com.securityc4po.api.BaseEntity import com.securityc4po.api.BaseEntity
import org.springframework.data.mongodb.core.mapping.Document
/* @Document(collection = "projects")
* @Document(collection = "project")
* Can be used after adding deps for mongodb
*/
open class ProjectEntity( open class ProjectEntity(
data: Project data: Project
) : BaseEntity<Project>(data) ) : 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 package com.securityc4po.api.project
import com.securityc4po.api.extensions.getLoggerFor import com.securityc4po.api.extensions.getLoggerFor
import org.junit.BeforeClass
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Flux import reactor.core.publisher.Mono
/* 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
@Service @Service
class ProjectService() { class ProjectService(private val projectRepository: ProjectRepository) {
var logger = getLoggerFor<ProjectService>() var logger = getLoggerFor<ProjectService>()
/* Remove after database is integrated */
val mapper = jacksonObjectMapper()
@BeforeClass
fun init() {
mapper.registerKotlinModule()
mapper.registerModule(JavaTimeModule())
}
/** /**
* Get all [Project]s * Get all [Project]s
* *
* @return list of [Project] * @return list of [Project]
*/ */
fun getProjects(): List<Project> { fun getProjects(): Mono<List<Project>> {
val jsonProjectsString: String = File("./src/main/resources/mocks/projects.json").readText(Charsets.UTF_8) return projectRepository.findAll().collectList().map {
val jsonProjectList: List<Project> = mapper.readValue<List<Project>>(jsonProjectsString) it.map { projectEntity -> projectEntity.toProject() }
/* After database integration the return should be Flux of ProjectEntity */ }
return jsonProjectList;
} }
} }

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 ## ## General Properties ##
spring.main.web-application-type=reactive # spring.main.web-application-type=reactive
spring.main.allow-bean-definition-overriding=true spring.main.allow-bean-definition-overriding=true
spring.jackson.default-property-inclusion=non_null
## Server Config ## ## Server Config ##
server.port=8443 server.port=8443
@ -11,13 +12,12 @@ management.endpoint.health.enabled=true
management.endpoints.web.exposure.include=info, health, metrics management.endpoints.web.exposure.include=info, health, metrics
## Database (MONGODB) Config ## ## Database (MONGODB) Config ##
# spring.data.mongodb.database=C4PO spring.data.mongodb.database=c4po
# spring.data.mongodb.host=localhost spring.data.mongodb.auto-index-creation=true
# spring.data.mongodb.port=27017
# spring.main.allow-bean-definition-overriding=true
# 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 spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local
keycloakhost=localhost 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 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.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.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) @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 { 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 @BeforeEach
fun setupDocs(restDocumentation: RestDocumentationContextProvider) { fun setupDocs(restDocumentation: RestDocumentationContextProvider) {
webTestClient = WebTestClient.bindToServer() webTestClient = WebTestClient.bindToServer()
.baseUrl("com.securityc4po.api.http://localhost:$port") .baseUrl("http://localhost:$port")
.filter(documentationConfiguration(restDocumentation)) .filter(documentationConfiguration(restDocumentation))
.responseTimeout(Duration.ofMillis(10000)) .responseTimeout(Duration.ofMillis(10000))
.build() .build()

View File

@ -8,4 +8,4 @@ import org.springframework.test.context.junit.jupiter.SpringExtension
@ExtendWith(SpringExtension::class) @ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext @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.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock 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.operation.preprocess.Preprocessors
import org.springframework.restdocs.payload.JsonFieldType import org.springframework.restdocs.payload.JsonFieldType
import org.springframework.restdocs.payload.PayloadDocumentation import org.springframework.restdocs.payload.PayloadDocumentation
@ -18,8 +19,8 @@ import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation
@SuppressFBWarnings(SIC_INNER_SHOULD_BE_STATIC) @SuppressFBWarnings(SIC_INNER_SHOULD_BE_STATIC)
class ProjectControllerDocumentationTest : BaseDocumentationIntTest() { class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
/*@Autowired @Autowired
lateinit var mongoTemplate: MongoTemplate*/ lateinit var mongoTemplate: MongoTemplate
@BeforeEach @BeforeEach
fun init() { 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.SIC_INNER_SHOULD_BE_STATIC
import com.securityc4po.api.configuration.URF_UNREAD_FIELD import com.securityc4po.api.configuration.URF_UNREAD_FIELD
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import io.netty.handler.ssl.SslContextBuilder
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.web.server.LocalServerPort import org.springframework.boot.web.server.LocalServerPort
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock 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.test.web.reactive.server.WebTestClient
import org.springframework.util.ResourceUtils
import reactor.netty.http.client.HttpClient
import java.time.Duration import java.time.Duration
@AutoConfigureWireMock(port = 0) @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() { class ProjectControllerIntTest : BaseIntTest() {
@LocalServerPort @LocalServerPort
private var port = 0 private var port = 0
@Autowired
lateinit var mongoTemplate: MongoTemplate
private lateinit var webTestClient: WebTestClient private lateinit var webTestClient: WebTestClient
@BeforeEach @BeforeEach
fun setupWebClient() { fun setupWebClient() {
webTestClient = WebTestClient.bindToServer() webTestClient = WebTestClient.bindToServer()
.baseUrl("http://localhost:$port") .baseUrl("http://localhost:$port")
.responseTimeout(Duration.ofMillis(10000)) .responseTimeout(Duration.ofMillis(10000))
.build() .build()
} }
/*@Autowired
lateinit var mongoTemplate: MongoTemplate*/
@BeforeEach @BeforeEach
fun init() { fun init() {
cleanUp() cleanUp()
configureAdminToken()
persistBasicTestScenario() persistBasicTestScenario()
} }
@ -44,63 +60,67 @@ class ProjectControllerIntTest : BaseIntTest() {
inner class GetProjects { inner class GetProjects {
@Test @Test
fun `requesting projects successfully`() { fun `requesting projects successfully`() {
/* Implement after the implementation of database */ webTestClient.get().uri("/v1/projects")
.header("Authorization", "Bearer $tokenAdmin")
/*webTestClient.get().uri("/v1/projects") .exchange()
.header("") .expectStatus().isOk
.exchange() .expectHeader().valueEquals("Application-Name", "security-c4po-api")
.expectStatus().isOk .expectBody().json(Json.write(getProjects()))
.expectHeader().doesNotExist("")
.expectBody().json(Json.write(getProjects()))*/
} }
val projectOne = Project( val projectOne = Project(
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9", id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
client = "E Corp", client = "E Corp",
title = "Some Mock API (v1.0) Scanning", title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z", createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester", tester = "Novatester",
logo = "Insert'E_Corp.png'BASE64Encoded" logo = "Insert'E_Corp.png'BASE64Encoded"
) )
val projectTwo = Project( val projectTwo = Project(
id = "61360a47-796b-4b3f-abf9-c46c668596c5", id = "61360a47-796b-4b3f-abf9-c46c668596c5",
client = "Allsafe", client = "Allsafe",
title = "CashMyData (iOS)", title = "CashMyData (iOS)",
createdAt = "2021-01-10T18:05:00Z", createdAt = "2021-01-10T18:05:00Z",
tester = "Elliot", tester = "Elliot",
logo = "Insert'Allsafe.png'BASE64Encoded" logo = "Insert'Allsafe.png'BASE64Encoded"
) )
private fun getProjects() = listOf( private fun getProjects() = listOf(
projectOne.toProjectResponseBody(), projectOne.toProjectResponseBody(),
projectTwo.toProjectResponseBody() projectTwo.toProjectResponseBody()
) )
} }
private fun cleanUp() { private fun cleanUp() {
/*mongoTemplate.findAllAndRemove(Query(), Project::class.java)*/ mongoTemplate.findAllAndRemove(Query(), Project::class.java)
tokenAdmin = "n/a"
} }
private fun persistBasicTestScenario() { private fun persistBasicTestScenario() {
// setup test data // setup test data
val projectOne = Project( val projectOne = Project(
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9", id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
client = "E Corp", client = "E Corp",
title = "Some Mock API (v1.0) Scanning", title = "Some Mock API (v1.0) Scanning",
createdAt = "2021-01-10T18:05:00Z", createdAt = "2021-01-10T18:05:00Z",
tester = "Novatester", tester = "Novatester",
logo = "Insert'E_Corp.png'BASE64Encoded" logo = "Insert'E_Corp.png'BASE64Encoded"
) )
val projectTwo = Project( val projectTwo = Project(
id = "61360a47-796b-4b3f-abf9-c46c668596c5", id = "61360a47-796b-4b3f-abf9-c46c668596c5",
client = "Allsafe", client = "Allsafe",
title = "CashMyData (iOS)", title = "CashMyData (iOS)",
createdAt = "2021-01-10T18:05:00Z", createdAt = "2021-01-10T18:05:00Z",
tester = "Elliot", tester = "Elliot",
logo = "Insert'Allsafe.png'BASE64Encoded" logo = "Insert'Allsafe.png'BASE64Encoded"
) )
cleanUp() cleanUp()
/*mongoTemplate.save(ProjectEntity(projectOne)) mongoTemplate.save(ProjectEntity(projectOne))
mongoTemplate.save(ProjectEntity(projectTwo))*/ 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 log = mock<Logger>()
private val cut = ProjectService().apply { private val projectRepository = mock<ProjectRepository>()
private val cut = ProjectService(projectRepository).apply {
this.logger = log 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: c4po-api:
build: '../../security-c4po-api' build: '../../security-c4po-api'
image: security-c4po-api:latest 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: deploy:
resources: resources:
limits: limits:
memory: "1G" memory: "1G"
ports: ports:
- '8443:8443' - 8443:8443
networks:
c4po:

View File

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

View File

@ -1,6 +1,6 @@
# cfg for local keycloak # cfg for local keycloak
DB_VENDOR=postgres DB_VENDOR=postgres
DB_ADDR=keycloak-postgres DB_ADDR=c4po-keycloak-postgress
DB_PORT=5432 DB_PORT=5432
DB_USER=c4po_kc_local DB_USER=c4po_kc_local
DB_PASSWORD=Test1234! 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: c4po-angular:
build: '../../security-c4po-angular' build: '../../security-c4po-angular'
image: security-c4po-angular:latest image: security-c4po-angular:latest
container_name: security-c4po-angular container_name: c4po-angular
depends_on:
- c4po-keycloak
deploy: deploy:
resources: resources:
limits: limits:
memory: "1G" memory: "1G"
ports: ports:
- '4200:4200' - 4200:4200
networks:
c4po:

View File

@ -2,23 +2,22 @@ version: '3.1'
services: services:
c4po-keycloak: c4po-keycloak:
container_name: security-c4po-keycloak container_name: c4po-keycloak
depends_on: depends_on:
- keycloak-postgres - c4po-keycloak-postgres
image: jboss/keycloak:11.0.3 image: jboss/keycloak:11.0.3
volumes: volumes:
- ../cfg/c4po_realm_export.json:/tmp/c4po_realm_export.json - ../cfg/c4po_realm_export.json:/tmp/c4po_realm_export.json
ports: ports:
- 8888:8080 - 8888:8080
- 9990:9990
env_file: env_file:
- ../cfg/keycloak.env - ../cfg/keycloak.env
keycloak-postgres: c4po-keycloak-postgress:
container_name: security-c4po-postgres-keycloak container_name: c4po-keycloak-postgres
image: postgres:latest image: postgres:latest
env_file: env_file:
- ../cfg/keycloakdb.env - ../cfg/keycloakdb.env
ports: ports:
- 5433:5432 - 5433:5432
volumes: 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