diff --git a/security-c4po-angular/src/environments/environment.prod.ts b/security-c4po-angular/src/environments/environment.prod.ts index 8eb2105..11270c0 100644 --- a/security-c4po-angular/src/environments/environment.prod.ts +++ b/security-c4po-angular/src/environments/environment.prod.ts @@ -2,7 +2,7 @@ export const environment = { production: true, // keycloak - keycloakURL: 'http://localhost:8888/auth', + keycloakURL: 'http://localhost:8080/auth', keycloakrealm: 'c4po_realm_local', keycloakclientId: 'c4po_local', diff --git a/security-c4po-angular/src/environments/environment.ts b/security-c4po-angular/src/environments/environment.ts index 53d9e43..927b751 100644 --- a/security-c4po-angular/src/environments/environment.ts +++ b/security-c4po-angular/src/environments/environment.ts @@ -7,7 +7,7 @@ export const environment = { production: false, // keycloak - keycloakURL: 'http://localhost:8888/auth', + keycloakURL: 'http://localhost:8080/auth', keycloakrealm: 'c4po_realm_local', keycloakclientId: 'c4po_local', diff --git a/security-c4po-api/build.gradle.kts b/security-c4po-api/build.gradle.kts index 9e74c6a..1f463d1 100644 --- a/security-c4po-api/build.gradle.kts +++ b/security-c4po-api/build.gradle.kts @@ -91,6 +91,7 @@ dependencies { testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient") testImplementation("com.github.spotbugs:spotbugs-annotations:4.1.2") testApi("org.testcontainers:junit-jupiter:1.15.2") + testImplementation("com.github.dasniko:testcontainers-keycloak:2.3.0") } jacoco { diff --git a/security-c4po-api/security-c4po-api.postman_collection.json b/security-c4po-api/security-c4po-api.postman_collection.json index 6f7b2ac..2ea4fd6 100644 --- a/security-c4po-api/security-c4po-api.postman_collection.json +++ b/security-c4po-api/security-c4po-api.postman_collection.json @@ -175,12 +175,12 @@ "method": "GET", "header": [], "url": { - "raw": "http://localhost:8888/auth/realms/c4po_realm_local/.well-known/openid-configuration", + "raw": "http://localhost:8080/auth/realms/c4po_realm_local/.well-known/openid-configuration", "protocol": "http", "host": [ "localhost" ], - "port": "8888", + "port": "8080", "path": [ "auth", "realms", @@ -235,12 +235,12 @@ ] }, "url": { - "raw": "http://localhost:8888/auth/realms/c4po_realm_local/protocol/openid-connect/token", + "raw": "http://localhost:8080/auth/realms/c4po_realm_local/protocol/openid-connect/token", "protocol": "http", "host": [ "localhost" ], - "port": "8888", + "port": "8080", "path": [ "auth", "realms", @@ -837,4 +837,4 @@ } } ] -} \ No newline at end of file +} diff --git a/security-c4po-api/src/main/resources/application-COMPOSE.properties b/security-c4po-api/src/main/resources/application-COMPOSE.properties index e4888d6..2b2f4cc 100644 --- a/security-c4po-api/src/main/resources/application-COMPOSE.properties +++ b/security-c4po-api/src/main/resources/application-COMPOSE.properties @@ -6,4 +6,4 @@ keycloak.client.realm.path=auth/realms/c4po_realm_local/ ## Database (MONGODB) Config ## spring.data.mongodb.host=c4po-db -spring.data.mongodb.port=27017 \ No newline at end of file +spring.data.mongodb.port=27017 diff --git a/security-c4po-api/src/main/resources/application-DEV.properties b/security-c4po-api/src/main/resources/application-DEV.properties index 9c88551..179334b 100644 --- a/security-c4po-api/src/main/resources/application-DEV.properties +++ b/security-c4po-api/src/main/resources/application-DEV.properties @@ -1,8 +1,8 @@ ## IdentityProvider (Keycloak) ## -spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local +spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local keycloakhost=localhost -keycloak.client.url=http://localhost:8888/ +keycloak.client.url=http://localhost:8080/ ## Database (MONGODB) Config ## spring.data.mongodb.host=localhost -spring.data.mongodb.port=27017 \ No newline at end of file +spring.data.mongodb.port=27017 diff --git a/security-c4po-api/src/main/resources/application-TEST.properties b/security-c4po-api/src/main/resources/application-TEST.properties index d8e6f76..2264243 100644 --- a/security-c4po-api/src/main/resources/application-TEST.properties +++ b/security-c4po-api/src/main/resources/application-TEST.properties @@ -1,9 +1,7 @@ ## IdentityProvider (Keycloak) ## -spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9999/auth/realms/c4po_realm_local -keycloakhost=localhost keycloak.client.url=http://localhost:9999 keycloak.client.realm.path=auth/realms/c4po_realm_local/ ## Database (MONGODB) Config ## spring.data.mongodb.host=localhost -spring.data.mongodb.port=27021 \ No newline at end of file +spring.data.mongodb.port=27021 diff --git a/security-c4po-api/src/main/resources/application.properties b/security-c4po-api/src/main/resources/application.properties index 0eb5b31..c98625e 100644 --- a/security-c4po-api/src/main/resources/application.properties +++ b/security-c4po-api/src/main/resources/application.properties @@ -16,11 +16,11 @@ spring.data.mongodb.database=c4po spring.data.mongodb.auto-index-creation=true ## IdentityProvider (Keycloak) ## -spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/c4po_realm_local +spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/c4po_realm_local keycloakhost=localhost -keycloak.client.url=http://localhost:8888 +keycloak.client.url=http://localhost:8080 keycloak.client.realm.path=auth/realms/c4po_realm_local/ ## Total number of pentests listet in the OWASP testing guide ## https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v4.pdf -owasp.web.pentests=95 \ No newline at end of file +owasp.web.pentests=95 diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/BaseContainerizedTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/BaseContainerizedTest.kt index 281df22..7a148fd 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/BaseContainerizedTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/BaseContainerizedTest.kt @@ -5,46 +5,48 @@ 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 dasniko.testcontainers.keycloak.KeycloakContainer 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.ActiveProfiles +import org.springframework.test.context.DynamicPropertyRegistry +import org.springframework.test.context.DynamicPropertySource 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.containers.wait.strategy.Wait import org.testcontainers.images.builder.ImageFromDockerfile +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper 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=27017", - "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" -]) +@TestPropertySource( + properties = [ + "spring.data.mongodb.port=27017", + "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:4.4.6") - 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)) + val mongoDbContainer = KGenericContainer( + ImageFromDockerfile("c4poapibasecontainerizedtest").withDockerfileFromBuilder { + it.from("mongo:latest") + 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(27017), ExposedPort(27017))) @@ -52,28 +54,29 @@ abstract class BaseContainerizedTest { start() } - val keycloakContainer = KGenericContainerFromImage(DockerImageName.parse("jboss/keycloak:11.0.3")).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("realm-export.json", 700), "/tmp/realm.json") - start() - println("== Inserting users must wait until Keycloak is started completely ==") - execInContainer("sh", "/opt/jboss/create-keycloak-user.sh") + val keycloakContainer: KeycloakContainer = KeycloakContainer("quay.io/keycloak/keycloak:20.0.0") + .withAdminUsername("admin") + .withAdminPassword("admin") + .withContextPath("/auth") + .withRealmImportFile("realm-export.json") + .waitingFor( + Wait.forHttp("/auth") + ) + + @DynamicPropertySource + @JvmStatic + fun jwtValidationProperties(registry: DynamicPropertyRegistry) { + registry.add("spring.security.oauth2.resourceserver.jwt.issuer-uri") { keycloakContainer.authServerUrl + "/realms/c4po_realm_local" } + + keycloakContainer.start() } } 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 @@ -84,8 +87,11 @@ abstract class BaseContainerizedTest { map.add("password", password) map.add("grant_type", "password") map.add("client_secret", "secret") - val responseString = restTemplate.postForObject("$keycloakHost/auth/realms/$realm/protocol/openid-connect/token", - HttpEntity(map, headers), String::class.java) + val responseString = restTemplate.postForObject( + keycloakContainer.authServerUrl + "/realms/$realm/protocol/openid-connect/token", + HttpEntity(map, headers), + String::class.java + ) val token = ObjectMapper().readValue(responseString, KeyCloakToken::class.java) return token.access_token!! } @@ -101,4 +107,4 @@ abstract class BaseContainerizedTest { class KGenericContainerFromImage(imageName: DockerImageName) : GenericContainer(imageName) class KGenericContainer(dockerFile: ImageFromDockerfile) : GenericContainer(dockerFile) -} \ No newline at end of file +} diff --git a/security-c4po-api/src/test/resources/script_local b/security-c4po-api/src/test/resources/script_local index db262e0..6e09418 100644 --- a/security-c4po-api/src/test/resources/script_local +++ b/security-c4po-api/src/test/resources/script_local @@ -1,7 +1,7 @@ #!/usr/bin/env bash cd keycloak/bin sleep 20 -./kcadm.sh config credentials --server http://localhost:8888/auth --realm master --user admin --password admin +./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=testadmin@test.de \ diff --git a/security-c4po-cfg/cfg/keycloak.env b/security-c4po-cfg/cfg/keycloak.env deleted file mode 100644 index f65c089..0000000 --- a/security-c4po-cfg/cfg/keycloak.env +++ /dev/null @@ -1,9 +0,0 @@ -# cfg for local keycloak -DB_VENDOR=postgres -DB_ADDR=c4po-keycloak-postgress -DB_PORT=5432 -DB_USER=c4po_kc_local -DB_PASSWORD=Test1234! -KEYCLOAK_USER=admin -KEYCLOAK_PASSWORD=admin -KEYCLOAK_IMPORT=/tmp/c4po_realm_export.json diff --git a/security-c4po-cfg/cfg/keycloakdb.env b/security-c4po-cfg/cfg/keycloakdb.env deleted file mode 100644 index a649ddc..0000000 --- a/security-c4po-cfg/cfg/keycloakdb.env +++ /dev/null @@ -1,4 +0,0 @@ -# database.env -POSTGRES_USER=c4po_kc_local -POSTGRES_PASSWORD=Test1234! -POSTGRES_DB=keycloak diff --git a/security-c4po-cfg/docker-compose.yml b/security-c4po-cfg/docker-compose.yml index c82c6d7..c3935c3 100644 --- a/security-c4po-cfg/docker-compose.yml +++ b/security-c4po-cfg/docker-compose.yml @@ -5,23 +5,11 @@ volumes: 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 + - ./volumes/mongodb/data/:/db/data deploy: resources: limits: @@ -30,20 +18,15 @@ services: - 27017:27017 networks: - c4po - # Authentity Provider + # Authentication Provider c4po-keycloak: container_name: c4po-keycloak - depends_on: - - c4po-keycloak-postgres - links: - - c4po-keycloak-postgres - image: jboss/keycloak:11.0.3 + image: quay.io/keycloak/keycloak:20.0.0 volumes: - - /cfg/c4po_realm_export.json/:/tmp/c4po_realm_export.json + - ./cfg/c4po_realm_export.json/:/opt/keycloak/data/import/c4po_realm_export.json ports: - - 8888:8080 - env_file: - - cfg/keycloak.env + - 8080:8080 + entrypoint: /opt/keycloak/bin/kc.sh start-dev --import-realm --http-relative-path=/auth networks: - c4po # Services @@ -53,8 +36,6 @@ services: container_name: c4po-angular depends_on: - c4po-keycloak - links: - - c4po-keycloak deploy: resources: limits: @@ -63,7 +44,6 @@ services: - 4200:4200 networks: - c4po - c4po-api: build: '../security-c4po-api' image: security-c4po-api:latest @@ -73,9 +53,6 @@ services: depends_on: - c4po-db - c4po-keycloak - links: - - c4po-db - - c4po-keycloak deploy: resources: limits: