From 2d40cc07eee6c9755ee6c173c666e47e5cad4d0b Mon Sep 17 00:00:00 2001 From: barreiro Date: Fri, 19 Jan 2024 17:32:46 +0000 Subject: [PATCH] Fix dev service to start postgres only and allow horreum to start without oidc.auth-server-url --- .../server/HorreumAuthorizationFilter.java | 10 +- .../tools/horreum/svc/ConfigServiceImpl.java | 2 +- .../HorreumDevServicesProcessor.java | 89 +++++----- .../infra/common/HorreumResources.java | 168 +++++++++--------- .../src/main/resources/init-script.sql | 2 + 5 files changed, 139 insertions(+), 132 deletions(-) create mode 100644 infra/horreum-infra-common/src/main/resources/init-script.sql diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/server/HorreumAuthorizationFilter.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/server/HorreumAuthorizationFilter.java index 0922d51c2..ac939dd72 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/server/HorreumAuthorizationFilter.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/server/HorreumAuthorizationFilter.java @@ -19,10 +19,10 @@ @Singleton public class HorreumAuthorizationFilter { - private final String authServerUrl; + private final Optional authServerUrl; private final Optional issuer; - public HorreumAuthorizationFilter(@ConfigProperty(name = "quarkus.oidc.auth-server-url") String authServerUrl, + public HorreumAuthorizationFilter(@ConfigProperty(name = "quarkus.oidc.auth-server-url") Optional authServerUrl, @ConfigProperty(name = "quarkus.oidc.token.issuer") Optional issuer) { this.authServerUrl = authServerUrl; this.issuer = issuer; @@ -51,8 +51,10 @@ public Response filter(ResteasyReactiveContainerRequestContext containerRequestC } else if (!issuer.get().equals(iss)) { return replyWrongIss(iss, issuer.get()); } - } else if (!authServerUrl.equals(iss)) { - return replyWrongIss(iss, authServerUrl); + } else if (authServerUrl.isEmpty()) { + return Response.status(Response.Status.FORBIDDEN).entity("Missing URL to validate authorization token. Set OIDC authentication server URL (or OIDC token issuer) in Horreum config.").build(); + } else if (!authServerUrl.get().equals(iss)) { + return replyWrongIss(iss, authServerUrl.get()); } return null; } diff --git a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java index d1c9e01cb..b393b39cb 100644 --- a/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java +++ b/horreum-backend/src/main/java/io/hyperfoil/tools/horreum/svc/ConfigServiceImpl.java @@ -142,7 +142,7 @@ public void deleteDatastore(String datastoreId) { } private String getString(String propertyName) { - return ConfigProvider.getConfig().getValue(propertyName, String.class); + return ConfigProvider.getConfig().getOptionalValue(propertyName, String.class).orElse(""); } } diff --git a/infra/horreum-dev-services/deployment/src/main/java/io/hyperfoil/tools/horreum/dev/services/deployment/HorreumDevServicesProcessor.java b/infra/horreum-dev-services/deployment/src/main/java/io/hyperfoil/tools/horreum/dev/services/deployment/HorreumDevServicesProcessor.java index 97210928e..71a7af85a 100644 --- a/infra/horreum-dev-services/deployment/src/main/java/io/hyperfoil/tools/horreum/dev/services/deployment/HorreumDevServicesProcessor.java +++ b/infra/horreum-dev-services/deployment/src/main/java/io/hyperfoil/tools/horreum/dev/services/deployment/HorreumDevServicesProcessor.java @@ -99,51 +99,45 @@ public void startHorreumContainers( Map envvars = HorreumResources.startContainers(Collections.unmodifiableMap(containerArgs)); - Map postgresConfig = new HashMap<>(); - String jdbcUrl = HorreumResources.postgreSQLResource.getJdbcUrl(); - - postgresConfig.put("quarkus.datasource.jdbc.url", jdbcUrl); - postgresConfig.put("quarkus.datasource.migration.jdbc.url", jdbcUrl); - if (horreumBuildTimeConfig.postgres.sslEnabled) { - // see https://jdbc.postgresql.org/documentation/ssl/ for details - postgresConfig.put("quarkus.datasource.jdbc.additional-jdbc-properties.ssl", "true"); - postgresConfig.put("quarkus.datasource.jdbc.additional-jdbc-properties.sslmode", "verify-full"); - postgresConfig.put("quarkus.datasource.jdbc.additional-jdbc-properties.sslrootcert", envvars.get("quarkus.datasource.jdbc.sslrootcert")); + if (horreumBuildTimeConfig.postgres.enabled) { + Map postgresConfig = new HashMap<>(); + String jdbcUrl = HorreumResources.postgreSQLResource.getJdbcUrl(); + + postgresConfig.put("quarkus.datasource.jdbc.url", jdbcUrl); + postgresConfig.put("quarkus.datasource.migration.jdbc.url", jdbcUrl); + if (horreumBuildTimeConfig.postgres.sslEnabled) { + // see https://jdbc.postgresql.org/documentation/ssl/ for details + postgresConfig.put("quarkus.datasource.jdbc.additional-jdbc-properties.ssl", "true"); + postgresConfig.put("quarkus.datasource.jdbc.additional-jdbc-properties.sslmode", "verify-full"); + postgresConfig.put("quarkus.datasource.jdbc.additional-jdbc-properties.sslrootcert", envvars.get("quarkus.datasource.jdbc.sslrootcert")); + } + + horreumPostgresDevService = new DevServicesResultBuildItem.RunningDevService( + HorreumResources.postgreSQLResource.getContainer().getContainerName(), + HorreumResources.postgreSQLResource.getContainer().getContainerId(), + HorreumResources.postgreSQLResource.getContainer()::close, + postgresConfig); } - - horreumPostgresDevService = new DevServicesResultBuildItem.RunningDevService( - HorreumResources.postgreSQLResource.getContainer().getContainerName(), - HorreumResources.postgreSQLResource.getContainer().getContainerId(), - HorreumResources.postgreSQLResource.getContainer()::close, - postgresConfig); - - Map keycloakConfig = new HashMap<>(); - Integer keycloakPort = HorreumResources.keycloakResource.getContainer().getMappedPort(horreumBuildTimeConfig.keycloak.httpsEnabled ? 8443 : 8080); - String keycloakURL = (horreumBuildTimeConfig.keycloak.httpsEnabled ? "https" : "http") + "://localhost:" + keycloakPort; - - keycloakConfig.put("horreum.keycloak.url", keycloakURL); - keycloakConfig.put("quarkus.oidc.auth-server-url", keycloakURL + "/realms/horreum"); - keycloakConfig.put("quarkus.oidc.credentials.secret", envvars.get("quarkus.oidc.credentials.secret")); - if (envvars.containsKey("quarkus.oidc.tls.trust-store-file")) { - keycloakConfig.put("quarkus.oidc.tls.trust-store-file", envvars.get("quarkus.oidc.tls.trust-store-file")); - keycloakConfig.put("quarkus.oidc.tls.verification", "required"); // "certificate-validation" validates the certificate chain, but not the hostname. could also be "none" and disable TLS verification altogether + if (horreumBuildTimeConfig.keycloak.enabled) { + Map keycloakConfig = new HashMap<>(); + Integer keycloakPort = HorreumResources.keycloakResource.getContainer().getMappedPort(horreumBuildTimeConfig.keycloak.httpsEnabled ? 8443 : 8080); + String keycloakURL = (horreumBuildTimeConfig.keycloak.httpsEnabled ? "https" : "http") + "://localhost:" + keycloakPort; + + keycloakConfig.put("horreum.keycloak.url", keycloakURL); + keycloakConfig.put("quarkus.oidc.auth-server-url", keycloakURL + "/realms/horreum"); + keycloakConfig.put("quarkus.oidc.credentials.secret", envvars.get("quarkus.oidc.credentials.secret")); + if (envvars.containsKey("quarkus.oidc.tls.trust-store-file")) { + keycloakConfig.put("quarkus.oidc.tls.trust-store-file", envvars.get("quarkus.oidc.tls.trust-store-file")); + keycloakConfig.put("quarkus.oidc.tls.verification", "required"); // "certificate-validation" validates the certificate chain, but not the hostname. could also be "none" and disable TLS verification altogether + } + + horreumKeycloakDevService = new DevServicesResultBuildItem.RunningDevService( + HorreumResources.keycloakResource.getContainer().getContainerName(), + HorreumResources.keycloakResource.getContainer().getContainerId(), + HorreumResources.keycloakResource.getContainer()::close, + keycloakConfig); } - - horreumKeycloakDevService = new DevServicesResultBuildItem.RunningDevService( - HorreumResources.keycloakResource.getContainer().getContainerName(), - HorreumResources.keycloakResource.getContainer().getContainerId(), - HorreumResources.keycloakResource.getContainer()::close, - keycloakConfig); - } - } - - if (horreumKeycloakDevService == null || horreumPostgresDevService == null) { - if (!errors) { - compressor.close(); - } else { - compressor.closeAndDumpCaptured(); } - return; } Runnable closeTask = () -> { @@ -176,8 +170,15 @@ public void startHorreumContainers( throw new RuntimeException(t); } - devServicesResultBuildItemBuildProducer.produce(horreumKeycloakDevService.toBuildItem()); - devServicesResultBuildItemBuildProducer.produce(horreumPostgresDevService.toBuildItem()); + if (horreumPostgresDevService != null) { + devServicesResultBuildItemBuildProducer.produce(horreumPostgresDevService.toBuildItem()); + + // TODO: figure out how to run a SQL script to load dummy data into the database + // that has to execute *after* liquibase execution so that the DB schema has been generated + } + if (horreumKeycloakDevService != null) { + devServicesResultBuildItemBuildProducer.produce(horreumKeycloakDevService.toBuildItem()); + } } } diff --git a/infra/horreum-infra-common/src/main/java/io/hyperfoil/tools/horreum/infra/common/HorreumResources.java b/infra/horreum-infra-common/src/main/java/io/hyperfoil/tools/horreum/infra/common/HorreumResources.java index 3328358ec..4f7c5cc35 100644 --- a/infra/horreum-infra-common/src/main/java/io/hyperfoil/tools/horreum/infra/common/HorreumResources.java +++ b/infra/horreum-infra-common/src/main/java/io/hyperfoil/tools/horreum/infra/common/HorreumResources.java @@ -128,106 +128,108 @@ protected static String getProperty(String propertyName) { public static Map startContainers(Map initArgs) { Map envVariables = new HashMap<>(); - - String QUARKUS_DATASOURCE_PASSWORD = getProperty("quarkus.datasource.password"); - envVariables.put("QUARKUS_DATASOURCE_PASSWORD", QUARKUS_DATASOURCE_PASSWORD); - envVariables.put("QUARKUS_DATASOURCE_MIGRATION_PASSWORD", QUARKUS_DATASOURCE_PASSWORD); - - envVariables.put("STOP_SIGNAL", "SIGKILL"); - - envVariables.putAll(initArgs); - envVariables.put("inContainer", "true"); - - envVariables.put(HORREUM_DEV_DB_DATABASE, DEFAULT_DBDATABASE); - envVariables.put(HORREUM_DEV_DB_USERNAME, DEFAULT_DB_USERNAME); - envVariables.put(HORREUM_DEV_DB_PASSWORD, DEFAULT_DB_PASSWORD); - - postgreSQLResource.init(envVariables); - Optional optionalNetwork = Optional.of(network); - Map postgresEnv = postgreSQLResource.start(optionalNetwork); - - waitForContainerReady(postgreSQLResource.getContainer(), " database system is ready to accept connections"); - - envVariables.putAll(postgresEnv); - envVariables.putAll(postgresCertificateProperties(initArgs)); - - keycloakResource.init(envVariables); - Map keycloakEnv = keycloakResource.start(optionalNetwork); + if (Boolean.parseBoolean(initArgs.get(HORREUM_DEV_POSTGRES_ENABLED))) { + String QUARKUS_DATASOURCE_PASSWORD = getProperty("quarkus.datasource.password"); + envVariables.put("QUARKUS_DATASOURCE_PASSWORD", QUARKUS_DATASOURCE_PASSWORD); + envVariables.put("QUARKUS_DATASOURCE_MIGRATION_PASSWORD", QUARKUS_DATASOURCE_PASSWORD); - waitForContainerReady(keycloakResource.getContainer(), "started in"); + envVariables.put("STOP_SIGNAL", "SIGKILL"); - envVariables.put("keycloak.host", keycloakEnv.get("keycloak.host")); - envVariables.put("horreum.keycloak.url", keycloakEnv.get("keycloak.host")); - envVariables.put("quarkus.oidc.auth-server-url", keycloakEnv.get("keycloak.host").concat("/realms/").concat(HORREUM_REALM)); - envVariables.putAll(oidcTruststoreProperties(initArgs)); + envVariables.putAll(initArgs); + envVariables.put("inContainer", "true"); - String keycloakAdminUser = initArgs.get(HORREUM_DEV_KEYCLOAK_ADMIN_USERNAME); - String keycloakAdminPassword = initArgs.get(HORREUM_DEV_KEYCLOAK_ADMIN_PASSWORD); + envVariables.put(HORREUM_DEV_DB_DATABASE, DEFAULT_DBDATABASE); + envVariables.put(HORREUM_DEV_DB_USERNAME, DEFAULT_DB_USERNAME); + envVariables.put(HORREUM_DEV_DB_PASSWORD, DEFAULT_DB_PASSWORD); - keycloak = KeycloakBuilder.builder() - .serverUrl(keycloakEnv.get("keycloak.host")) - .realm(KEYCLOAK_REALM) - .username(keycloakAdminUser) - .password(keycloakAdminPassword) - .clientId("admin-cli") - .resteasyClient(((ResteasyClientBuilder) ClientBuilder.newBuilder()).disableTrustManager().build()) - .build(); + postgreSQLResource.init(envVariables); - if ( ! initArgs.containsKey(HORREUM_DEV_POSTGRES_BACKUP) ) { - // Not using a backup db, so need to create the dummy roles + Map postgresEnv = postgreSQLResource.start(optionalNetwork); - // Obtain client secrets for Horreum - envVariables.put("quarkus.oidc.credentials.secret", generateClientSecret.apply("horreum")); + waitForContainerReady(postgreSQLResource.getContainer(), " database system is ready to accept connections"); - // Create roles and example user in Keycloak - RoleRepresentation uploaderRole = getRoleID.apply("uploader"); - RoleRepresentation testerRole = getRoleID.apply("tester"); - RoleRepresentation viewerRole = getRoleID.apply("viewer"); - RoleRepresentation adminRole = getRoleID.apply("admin"); - - RoleRepresentation devTeamRole = createRole.apply(() -> RoleBuilder.create().name("dev-team").build()); - RoleRepresentation teamViewerRole = createRole.apply(() -> RoleBuilder.create().name("dev-viewer").composite().realmComposite(devTeamRole).realmComposite(viewerRole).build()); - RoleRepresentation teamUploaderRole = createRole.apply(() -> RoleBuilder.create().name("dev-uploader").composite().realmComposite(devTeamRole).realmComposite(uploaderRole).build()); - RoleRepresentation teamTesterRole = createRole.apply(() -> RoleBuilder.create().name("dev-tester").composite().realmComposite(devTeamRole).realmComposite(testerRole).build()); - RoleRepresentation teamManagerRole = createRole.apply(() -> RoleBuilder.create().name("dev-manager").composite().realmComposite(devTeamRole).build()); - - UserRepresentation dummyUser = createUser.apply(() -> - UserBuilder.create() - .username("user") - .firstName("Dummy") - .lastName("User") - .password("secret") - .email("user@example.com") - .enabled(true) - .build() - ); + envVariables.putAll(postgresEnv); + envVariables.putAll(postgresCertificateProperties(initArgs)); + } + if (Boolean.parseBoolean(initArgs.get(HORREUM_DEV_KEYCLOAK_ENABLED))) { + keycloakResource.init(envVariables); + Map keycloakEnv = keycloakResource.start(optionalNetwork); + + waitForContainerReady(keycloakResource.getContainer(), "started in"); + + envVariables.put("keycloak.host", keycloakEnv.get("keycloak.host")); + envVariables.put("horreum.keycloak.url", keycloakEnv.get("keycloak.host")); + envVariables.put("quarkus.oidc.auth-server-url", keycloakEnv.get("keycloak.host").concat("/realms/").concat(HORREUM_REALM)); + envVariables.putAll(oidcTruststoreProperties(initArgs)); + + String keycloakAdminUser = initArgs.get(HORREUM_DEV_KEYCLOAK_ADMIN_USERNAME); + String keycloakAdminPassword = initArgs.get(HORREUM_DEV_KEYCLOAK_ADMIN_PASSWORD); + + keycloak = KeycloakBuilder.builder() + .serverUrl(keycloakEnv.get("keycloak.host")) + .realm(KEYCLOAK_REALM) + .username(keycloakAdminUser) + .password(keycloakAdminPassword) + .clientId("admin-cli") + .resteasyClient(((ResteasyClientBuilder) ClientBuilder.newBuilder()).disableTrustManager().build()) + .build(); + + if (!initArgs.containsKey(HORREUM_DEV_POSTGRES_BACKUP)) { + // Not using a backup db, so need to create the dummy roles + + // Obtain client secrets for Horreum + envVariables.put("quarkus.oidc.credentials.secret", generateClientSecret.apply("horreum")); + + // Create roles and example user in Keycloak + RoleRepresentation uploaderRole = getRoleID.apply("uploader"); + RoleRepresentation testerRole = getRoleID.apply("tester"); + RoleRepresentation viewerRole = getRoleID.apply("viewer"); + RoleRepresentation adminRole = getRoleID.apply("admin"); + + RoleRepresentation devTeamRole = createRole.apply(() -> RoleBuilder.create().name("dev-team").build()); + RoleRepresentation teamViewerRole = createRole.apply(() -> RoleBuilder.create().name("dev-viewer").composite().realmComposite(devTeamRole).realmComposite(viewerRole).build()); + RoleRepresentation teamUploaderRole = createRole.apply(() -> RoleBuilder.create().name("dev-uploader").composite().realmComposite(devTeamRole).realmComposite(uploaderRole).build()); + RoleRepresentation teamTesterRole = createRole.apply(() -> RoleBuilder.create().name("dev-tester").composite().realmComposite(devTeamRole).realmComposite(testerRole).build()); + RoleRepresentation teamManagerRole = createRole.apply(() -> RoleBuilder.create().name("dev-manager").composite().realmComposite(devTeamRole).build()); + + UserRepresentation dummyUser = createUser.apply(() -> + UserBuilder.create() + .username("user") + .firstName("Dummy") + .lastName("User") + .password("secret") + .email("user@example.com") + .enabled(true) + .build() + ); - keycloak.realm(HORREUM_REALM).users().get(dummyUser.getId()).roles().realmLevel().add(Arrays.asList(teamUploaderRole, teamTesterRole, teamViewerRole, teamManagerRole, adminRole)); + keycloak.realm(HORREUM_REALM).users().get(dummyUser.getId()).roles().realmLevel().add(Arrays.asList(teamUploaderRole, teamTesterRole, teamViewerRole, teamManagerRole, adminRole)); - ClientRepresentation accountClient = findClient.apply("account"); + ClientRepresentation accountClient = findClient.apply("account"); - RoleRepresentation viewProfileRole = getClientRoleID.apply(accountClient.getId(), "view-profile"); + RoleRepresentation viewProfileRole = getClientRoleID.apply(accountClient.getId(), "view-profile"); - keycloak.realm(HORREUM_REALM).users().get(dummyUser.getId()).roles().clientLevel(accountClient.getId()).add(Collections.singletonList(viewProfileRole)); - } + keycloak.realm(HORREUM_REALM).users().get(dummyUser.getId()).roles().clientLevel(accountClient.getId()).add(Collections.singletonList(viewProfileRole)); + } - //update running keycloak realm with dev services configuration - try { - Config config = ConfigProvider.getConfig(); - String httpPort = config.getOptionalValue("quarkus.http.port", String.class).orElse("8080"); - String httpHost = config.getOptionalValue("quarkus.http.host", String.class).orElse("localhost"); + //update running keycloak realm with dev services configuration + try { + Config config = ConfigProvider.getConfig(); + String httpPort = config.getOptionalValue("quarkus.http.port", String.class).orElse("8080"); + String httpHost = config.getOptionalValue("quarkus.http.host", String.class).orElse("localhost"); - ClientRepresentation clientRepresentation = keycloak.realm(HORREUM_REALM).clients().findByClientId("horreum-ui").get(0); - clientRepresentation.getWebOrigins().add("http://".concat(httpHost).concat(":").concat(httpPort)); - clientRepresentation.getRedirectUris().add("http://".concat(httpHost).concat(":").concat(httpPort).concat("/*")); + ClientRepresentation clientRepresentation = keycloak.realm(HORREUM_REALM).clients().findByClientId("horreum-ui").get(0); + clientRepresentation.getWebOrigins().add("http://".concat(httpHost).concat(":").concat(httpPort)); + clientRepresentation.getRedirectUris().add("http://".concat(httpHost).concat(":").concat(httpPort).concat("/*")); - envVariables.put("quarkus.oidc.credentials.secret", getClientSecret.apply("horreum")); + envVariables.put("quarkus.oidc.credentials.secret", getClientSecret.apply("horreum")); - keycloak.realm(HORREUM_REALM).clients().get(clientRepresentation.getId()).update(clientRepresentation); - } catch (Exception e){ - log.error("Unable to re-configure keycloak instance: ".concat(e.getLocalizedMessage())); + keycloak.realm(HORREUM_REALM).clients().get(clientRepresentation.getId()).update(clientRepresentation); + } catch (Exception e) { + log.error("Unable to re-configure keycloak instance: ".concat(e.getLocalizedMessage())); + } } log.info("Waiting for test infrastructure to start"); diff --git a/infra/horreum-infra-common/src/main/resources/init-script.sql b/infra/horreum-infra-common/src/main/resources/init-script.sql new file mode 100644 index 000000000..38fc1e078 --- /dev/null +++ b/infra/horreum-infra-common/src/main/resources/init-script.sql @@ -0,0 +1,2 @@ +-- TODO: this file is does not have any effect at the moment +-- in the future it should contain SQL to be executed by Quarkus to provision some dummy data into the postgres dev service