Skip to content

Commit 7381cd2

Browse files
feat: make frontend configurable via backend API
* feat: implement runtime env var configuration via config.js file # Conflicts: # frontend/nginx.conf * chore: ignore missing import for linting * docs: update README.md * refactor: move frontend configuration to env vars instead of /configuration endpoint * "revert: move frontend configuration to env vars instead of /configuration endpoint" This reverts commit 104eca9. * feat: remove docker specific code and entrypoint * feat: configure frontend via backend endpoint again * test: add missing configuration options * test: add test for ConfigurationMapper * test: add test for auth.service.ts * docs: remove obsolete README section * docs: fix typo
1 parent c3d8685 commit 7381cd2

21 files changed

Lines changed: 269 additions & 71 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package ch.puzzle.pcts.configuration;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
import org.springframework.validation.annotation.Validated;
6+
7+
@Validated
8+
@ConfigurationProperties(prefix = "pcts.frontend.keycloak")
9+
public record KeycloakConfiguration(@NotBlank String url, @NotBlank String realm, @NotBlank String clientId) {
10+
}

backend/src/main/java/ch/puzzle/pcts/dto/configuration/ConfigurationDto.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@
44
import java.util.List;
55

66
public record ConfigurationDto(
7-
@Schema(description = "All roles that are allowed to access admin functionality in the frontend", example = "org_hr") List<String> adminAuthorities) {
7+
@Schema(description = "Keycloak configuration for frontend authentication") KeycloakDto keycloak) {
8+
9+
public record KeycloakDto(String url, String realm, String clientId,
10+
@Schema(description = "All roles that are allowed to access admin functionality in the frontend", example = "org_hr") List<String> adminAuthorities) {
11+
}
812
}
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
package ch.puzzle.pcts.mapper;
22

33
import ch.puzzle.pcts.configuration.AuthorizationConfiguration;
4+
import ch.puzzle.pcts.configuration.KeycloakConfiguration;
45
import ch.puzzle.pcts.dto.configuration.ConfigurationDto;
56
import org.springframework.stereotype.Component;
67

78
@Component
89
public class ConfigurationMapper {
10+
private final KeycloakConfiguration keycloakConfig;
11+
12+
public ConfigurationMapper(KeycloakConfiguration keycloakConfig) {
13+
this.keycloakConfig = keycloakConfig;
14+
}
15+
916
public ConfigurationDto toDto(AuthorizationConfiguration authConfig) {
10-
return new ConfigurationDto(authConfig.adminAuthorities());
17+
return new ConfigurationDto(new ConfigurationDto.KeycloakDto(keycloakConfig.url(),
18+
keycloakConfig.realm(),
19+
keycloakConfig.clientId(),
20+
authConfig.adminAuthorities()));
1121
}
1222
}

backend/src/main/resources/application-dev.properties

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,9 @@ spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8544/real
3434
# Authentication & JWT
3535
pcts.security.authentication.display-name-claim=name
3636
pcts.security.authorization.admin-authorities=org_hr,org_gl
37-
pcts.security.authorization.authorities-spel-expression=[pitc][roles]
37+
pcts.security.authorization.authorities-spel-expression=[pitc][roles]
38+
39+
# Keycloak config exposed to frontend
40+
pcts.frontend.keycloak.url=http://localhost:8544
41+
pcts.frontend.keycloak.realm=pitc
42+
pcts.frontend.keycloak.client-id=pcts-dev

backend/src/main/resources/application-e2e-test.properties

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,9 @@ app.member-sync.password=
2424
# Authentication & JWT
2525
pcts.security.authentication.display-name-claim=name
2626
pcts.security.authorization.admin-authorities=org_hr,org_gl
27-
pcts.security.authorization.authorities-spel-expression=[pitc][roles]
27+
pcts.security.authorization.authorities-spel-expression=[pitc][roles]
28+
29+
# Keycloak config exposed to frontend
30+
pcts.frontend.keycloak.url=http://localhost:8544
31+
pcts.frontend.keycloak.realm=pitc
32+
pcts.frontend.keycloak.client-id=pcts-dev
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
springdoc.api-docs.enabled=false
22
springdoc.show-actuator=true
3+
4+
# Keycloak config exposed to frontend
5+
pcts.frontend.keycloak.url=https://sso.puzzle.ch/auth
6+
pcts.frontend.keycloak.realm=pitc
7+
pcts.frontend.keycloak.client-id=pitc_pcts_staging

backend/src/main/resources/application-staging.properties

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,9 @@ springdoc.swagger-ui.path=/api/docs
1313
# Authentication & JWT
1414
pcts.security.authentication.display-name-claim=name
1515
pcts.security.authorization.authorities-spel-expression=[pitc][roles]
16-
pcts.security.authorization.admin-authorities=org_hr,org_gl
16+
pcts.security.authorization.admin-authorities=org_hr,org_gl
17+
18+
# Keycloak config exposed to frontend
19+
pcts.frontend.keycloak.url=https://sso.puzzle.ch/auth
20+
pcts.frontend.keycloak.realm=pitc
21+
pcts.frontend.keycloak.client-id=pitc_pcts_staging

backend/src/test/java/ch/puzzle/pcts/controller/ConfigurationControllerIT.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ class ConfigurationControllerIT extends ControllerITBase {
3535

3636
@BeforeEach
3737
void setUp() {
38-
this.configurationDto = new ConfigurationDto(List.of("ADMIN_1", "ADMIN_2"));
38+
this.configurationDto = new ConfigurationDto(new ConfigurationDto.KeycloakDto("http://localhost:8544",
39+
"pitc",
40+
"pcts-dev",
41+
List.of("ADMIN_1", "ADMIN_2")));
3942
}
4043

4144
@DisplayName("Should successfully get configuration")
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package ch.puzzle.pcts.mapper;
2+
3+
import static ch.puzzle.pcts.util.TestData.*;
4+
import static ch.puzzle.pcts.util.TestDataModels.*;
5+
import static org.junit.jupiter.api.Assertions.*;
6+
7+
import ch.puzzle.pcts.dto.configuration.ConfigurationDto;
8+
import org.junit.jupiter.api.BeforeEach;
9+
import org.junit.jupiter.api.DisplayName;
10+
import org.junit.jupiter.api.Test;
11+
12+
class ConfigurationMapperTest {
13+
14+
private ConfigurationMapper configurationMapper;
15+
16+
@BeforeEach
17+
void setUp() {
18+
configurationMapper = new ConfigurationMapper(KEYCLOAK_CONFIGURATION);
19+
}
20+
21+
@DisplayName("Should return configuration dto with keycloak and authorization details")
22+
@Test
23+
void shouldReturnConfigurationDto() {
24+
ConfigurationDto result = configurationMapper.toDto(AUTHORIZATION_CONFIGURATION);
25+
26+
assertNotNull(result);
27+
assertNotNull(result.keycloak());
28+
assertEquals(KEYCLOAK_URL, result.keycloak().url());
29+
assertEquals(KEYCLOAK_REALM, result.keycloak().realm());
30+
assertEquals(KEYCLOAK_CLIENT_ID, result.keycloak().clientId());
31+
assertEquals(ADMIN_AUTHORITIES, result.keycloak().adminAuthorities());
32+
}
33+
}

backend/src/test/java/ch/puzzle/pcts/util/TestData.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,19 @@
22

33
import java.math.BigDecimal;
44
import java.time.LocalDate;
5+
import java.util.List;
56

67
public class TestData {
78

89
private TestData() {
910
}
1011

12+
public static final String KEYCLOAK_URL = "https://keycloak.example.com";
13+
public static final String KEYCLOAK_REALM = "test-realm";
14+
public static final String KEYCLOAK_CLIENT_ID = "test-client";
15+
public static final String AUTHORITIES_SP_EL_EXPRESSION = "[pitc][roles]";
16+
public static final List<String> ADMIN_AUTHORITIES = List.of("org_hr", "org_admin");
17+
1118
public static final LocalDate DATE_NOW = LocalDate.now();
1219
public static final LocalDate DATE_YESTERDAY = DATE_NOW.minusDays(1);
1320
public static final LocalDate DATE_TOMORROW = DATE_NOW.plusDays(1);

0 commit comments

Comments
 (0)