From 1a0e404249469d1cad11ca0092b25801a9b44390 Mon Sep 17 00:00:00 2001 From: Lunga Tsewu <1998tsewu@gmail.com> Date: Thu, 18 Jul 2024 21:12:04 +0200 Subject: [PATCH] Added security for admin roles and access between servers through docker network --- config-server/pom.xml | 4 ++ .../configserver/ConfigServerApplication.java | 2 + .../src/main/resources/application.yml | 18 ++++-- .../main/resources/config/discoveryserver.yml | 4 +- .../main/resources/config/gatewayserver.yml | 14 ++--- .../src/main/resources/application.yml | 4 +- docker-compose-servers.yml | 10 --- .../src/main/resources/application.yml | 9 +-- .../src/main/resources/application.yml | 9 +-- .../config/KeycloakRoleConverter.java | 6 +- .../gatewayserver/config/RoutesConfig.java | 63 ++++++++++++------- .../gatewayserver/config/SecurityConfig.java | 15 +++-- .../src/main/resources/application.yml | 8 ++- .../src/main/resources/application.yml | 9 +-- 14 files changed, 97 insertions(+), 78 deletions(-) diff --git a/config-server/pom.xml b/config-server/pom.xml index d213487..9670e15 100644 --- a/config-server/pom.xml +++ b/config-server/pom.xml @@ -20,6 +20,10 @@ org.springframework.cloud spring-cloud-config-monitor + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + org.springframework.boot spring-boot-starter-actuator diff --git a/config-server/src/main/java/com/configserver/ConfigServerApplication.java b/config-server/src/main/java/com/configserver/ConfigServerApplication.java index 5db4c8a..3cd9bd0 100644 --- a/config-server/src/main/java/com/configserver/ConfigServerApplication.java +++ b/config-server/src/main/java/com/configserver/ConfigServerApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer +@EnableDiscoveryClient public class ConfigServerApplication { public static void main(String[] args) { diff --git a/config-server/src/main/resources/application.yml b/config-server/src/main/resources/application.yml index 64a8b53..c8a8eaa 100644 --- a/config-server/src/main/resources/application.yml +++ b/config-server/src/main/resources/application.yml @@ -47,10 +47,20 @@ spring: properties: topic: replication-factor: 1 - - -server: - port: 8071 +eureka: + instance: + hostname: discoveryServerApp + preferIpAddress: true + client: + fetch-registry: true + register-with-eureka: true + serviceUrl: + defaultZone: http://${eureka.instance.hostname}:8080/eureka/ + healthcheck: + enabled: true +endpoints: + shutdown: + enabled: true encrypt: key: 7E93D539EAA5C42F8523AB1627D1B management: diff --git a/config-server/src/main/resources/config/discoveryserver.yml b/config-server/src/main/resources/config/discoveryserver.yml index 33fb4e2..43dfe0e 100644 --- a/config-server/src/main/resources/config/discoveryserver.yml +++ b/config-server/src/main/resources/config/discoveryserver.yml @@ -1,10 +1,10 @@ server: - port: 8070 + port: 8080 eureka: instance: hostname: discoveryServerApp client: fetch-registry: false - register-with-eureka: false + register-with-eureka: true serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ \ No newline at end of file diff --git a/config-server/src/main/resources/config/gatewayserver.yml b/config-server/src/main/resources/config/gatewayserver.yml index 3732ff3..5cd7cd0 100644 --- a/config-server/src/main/resources/config/gatewayserver.yml +++ b/config-server/src/main/resources/config/gatewayserver.yml @@ -1,11 +1,9 @@ -server: - port: 8072 -spring: - security: - oauth2: - resourceserver: - jwt: - jwk-set-uri: "http://keyClock:8080/realms/master/protocol/openid-connect/certs" +#spring: +# security: +# oauth2: +# resourceserver: +# jwt: +# jwk-set-uri: "http://keyClock:8080/realms/master/protocol/openid-connect/certs" resilience4j.circuitbreaker: configs: default: diff --git a/discovery-server/src/main/resources/application.yml b/discovery-server/src/main/resources/application.yml index 2deb711..c45965c 100644 --- a/discovery-server/src/main/resources/application.yml +++ b/discovery-server/src/main/resources/application.yml @@ -2,11 +2,11 @@ spring: application: name: discoveryserver config: - import: "configserver:http://configServerApp:8071/" + import: "configserver:http://configServerApp:8080/" cloud: config: enabled: true - uri: "http://configServerApp:8071" + uri: "http://configServerApp:8080" stream: kafka: binder: diff --git a/docker-compose-servers.yml b/docker-compose-servers.yml index acff9ab..bffd18c 100644 --- a/docker-compose-servers.yml +++ b/docker-compose-servers.yml @@ -9,8 +9,6 @@ services: context: ./config-server image: pdf_editor_config:latest container_name: configServerApp - ports: - - "8071:8071" networks: - server_net @@ -21,8 +19,6 @@ services: context: ./discovery-server image: pdf_editor_discover:latest container_name: discoveryServerApp - ports: - - "8070:8070" depends_on: - configServerApp networks: @@ -48,8 +44,6 @@ services: context: ./document-service image: pdf-editor-document:latest container_name: documentsServerApp - ports: - - "8082:8082" depends_on: - configServerApp - discoveryServerApp @@ -64,8 +58,6 @@ services: context: ./email-service image: pdf-editor-email:latest container_name: emailServerApp - ports: - - "8083:8083" depends_on: - configServerApp - discoveryServerApp @@ -79,8 +71,6 @@ services: image: pdf-editor-user:latest container_name: usersServerApp hostname: usersServerApp - ports: - - "8080:8080" depends_on: - configServerApp - discoveryServerApp diff --git a/document-service/src/main/resources/application.yml b/document-service/src/main/resources/application.yml index edf7334..de84986 100644 --- a/document-service/src/main/resources/application.yml +++ b/document-service/src/main/resources/application.yml @@ -2,11 +2,11 @@ spring: application: name: "documents" config: - import: "configserver:http://configServerApp:8071/" + import: "configserver:http://configServerApp:8080/" cloud: config: enabled: true - uri: "http://configServerApp:8071" + uri: "http://configServerApp:8080" stream: kafka: binder: @@ -29,9 +29,6 @@ spring: topics: download-document-name: download-document-event - -server: - port: 8082 management: endpoints: web: @@ -52,7 +49,7 @@ eureka: fetch-registry: true register-with-eureka: true serviceUrl: - defaultZone: http://${eureka.instance.hostname}:8070/eureka/ + defaultZone: http://${eureka.instance.hostname}:8080/eureka/ healthcheck: enabled: true endpoints: diff --git a/email-service/src/main/resources/application.yml b/email-service/src/main/resources/application.yml index 30dbe72..408453f 100644 --- a/email-service/src/main/resources/application.yml +++ b/email-service/src/main/resources/application.yml @@ -7,14 +7,14 @@ spring: application: name: "email" config: - import: "configserver:http://configServerApp:8071/" + import: "configserver:http://configServerApp:8080/" jackson: parser: allow-unquoted-field-names: true cloud: config: enabled: true - uri: http://configServerApp:8071 + uri: http://configServerApp:8080 stream: kafka: binder: @@ -30,9 +30,6 @@ spring: topics: retry: "download-document-event.RETRY" dead: "download-document-event.DEAD" - -server: - port: 8083 management: endpoints: web: @@ -53,7 +50,7 @@ eureka: fetch-registry: true register-with-eureka: true serviceUrl: - defaultZone: http://${eureka.instance.hostname}:8070/eureka/ + defaultZone: http://${eureka.instance.hostname}:8080/eureka/ healthcheck: enabled: true endpoints: diff --git a/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java b/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java index 537c933..e529d20 100644 --- a/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java +++ b/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java @@ -7,20 +7,20 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; public class KeycloakRoleConverter implements Converter> { @Override public Collection convert(Jwt source) { - Map> realmAccess = (Map>) source.getClaims().get("realm_access"); + Map> realmAccess = (Map>) source.getClaims().get("realm_access"); if(realmAccess==null||realmAccess.isEmpty()){ return new ArrayList<>(); } return realmAccess.get("roles") .stream().map(roleName->new SimpleGrantedAuthority("ROLE_"+roleName)) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } diff --git a/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java b/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java index 5bad45d..4badf8d 100644 --- a/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java +++ b/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java @@ -4,51 +4,68 @@ import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory; import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder; import org.springframework.cloud.client.circuitbreaker.Customizer; +import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.Buildable; +import org.springframework.cloud.gateway.route.builder.PredicateSpec; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import java.time.Duration; +import java.util.function.Function; @Configuration public class RoutesConfig { + + @Bean public RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes(). route(predicateSpec -> predicateSpec.path("/pdf-editor/users/**"). filters(filterSpec-> - { - - return filterSpec.rewritePath("/pdf-editor/users/(?.*)", "/pdf-editor/users/${segment}") + filterSpec.rewritePath("/pdf-editor/users/(?.*)", "/pdf-editor/users/${segment}") .circuitBreaker(circuitConfi -> circuitConfi.setName("userServiceCircuitBreaker") .setFallbackUri("/pdf-editor/fallback/contact-team") - ); - - }) - .uri("lb://USERS")) - .route(predicateSpec -> predicateSpec.path("/pdf-editor/documents/**"). - filters(filterSpec->filterSpec.rewritePath("/pdf-editor/documents/(?.*)","/pdf-editor/documents/${segment}") - .circuitBreaker(circuitConfi-> - circuitConfi.setName("documentsCircuitBreaker") - .setFallbackUri("/pdf-editor/fallback/contact-team") - .addStatusCode(HttpStatus.SERVICE_UNAVAILABLE.name()) - )) - .uri("lb://DOCUMENTS")) - .route(predicateSpec -> predicateSpec.path("/pdf-editor/emails/**"). - filters(filterSpec->filterSpec.rewritePath("/pdf-editor/emails/(?.*)","/pdf-editor/email/${segment}") - .circuitBreaker(circuitConfi-> - circuitConfi.setName("emailCircuitBreaker") - .setFallbackUri("/pdf-editor/fallback/contact-team") - .addStatusCode(HttpStatus.SERVICE_UNAVAILABLE.name()) - )) - .uri("lb://EMAIL")) + ) + ).uri("lb://USERS")) + .route( + buildRoute("/pdf-editor/documents/**","/pdf-editor/documents/(?.*)", + "/pdf-editor/documents/${segment}","documentsCircuitBreaker" + ,"/pdf-editor/fallback/contact-team","lb://DOCUMENTS") + ) + .route(buildRoute("/pdf-editor/emails/**","/pdf-editor/emails/(?.*)", + "/pdf-editor/emails/${segment}","emailCircuitBreaker" + ,"/pdf-editor/fallback/contact-team","lb://EMAILS") + + ) + .route(buildRoute("/pdf-editor/configserver/**","/pdf-editor/configserver/(?.*)", + "/${segment}","configServerCircuitBreaker" + ,"/pdf-editor/fallback/contact-team","lb://CONFIGSERVER") + + ) + .route(buildRoute("/pdf-editor/discoveryserver/**","/pdf-editor/discoveryserver/(?.*)", + "/${segment}","discoveryServerCircuitBreaker" + ,"/pdf-editor/fallback/contact-team","lb://DISCOVERYSERVER") + + ) .build(); } + private Function> buildRoute(String path, String rewritePath,String replacementPath, String circuitBreakerName, String fallbackUri, + String loadBalanceUri){ + return predicateSpec -> predicateSpec.path(path). + filters(filterSpec->filterSpec.rewritePath(rewritePath,replacementPath) + .circuitBreaker(circuitConfi-> + circuitConfi.setName(circuitBreakerName) + .setFallbackUri(fallbackUri) + .addStatusCode(HttpStatus.SERVICE_UNAVAILABLE.name()) + )) + .uri(loadBalanceUri); + } @Bean public Customizer circuitBreakerFactoryCustomizer(){ return factory->factory.configureDefault( diff --git a/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java b/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java index 13bced9..8ef94ae 100644 --- a/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java +++ b/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java @@ -3,7 +3,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; -import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; @@ -13,17 +12,23 @@ import org.springframework.security.web.server.SecurityWebFilterChain; import reactor.core.publisher.Mono; +import static com.gatewayserver.config.Roles.ADMIN; +import static com.gatewayserver.config.Roles.CLIENT; + @Configuration @EnableWebSecurity public class SecurityConfig { - private final String[] PATH_TO_AUTHENTICATE={"pdf-editor/users/**","pdf-editor/documents/**","pdf-editor/emails/**"}; + private final String[] USER_PATH_TO_AUTHENTICATE={"pdf-editor/users/**","pdf-editor/documents/**","pdf-editor/emails/**"}; + private final String[] ADMIN_PATH_TO_AUTHENTICATE={"/pdf-editor/discoveryserver/**","/pdf-editor/configserver/**"}; + @Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity){ httpSecurity. - authorizeExchange(exchange-> - exchange.pathMatchers(HttpMethod.GET).permitAll() - .pathMatchers(PATH_TO_AUTHENTICATE).hasAnyRole(Roles.ADMIN.name(),Roles.CLIENT.name()) + authorizeExchange(exchange->exchange + //exchange.pathMatchers(HttpMethod.GET).permitAll() + .pathMatchers(USER_PATH_TO_AUTHENTICATE).hasAnyRole(ADMIN.name(),CLIENT.name()) + .pathMatchers(ADMIN_PATH_TO_AUTHENTICATE).hasRole(ADMIN.name()) ) diff --git a/gateway-server/src/main/resources/application.yml b/gateway-server/src/main/resources/application.yml index a9b1098..4a1fd36 100644 --- a/gateway-server/src/main/resources/application.yml +++ b/gateway-server/src/main/resources/application.yml @@ -1,12 +1,14 @@ +server: + port: 8072 spring: application: name: "gatewayserver" config: - import: "configserver:http://configServerApp:8071/" + import: "configserver:http://configServerApp:8080/" cloud: config: enabled: true - uri: http://configServerApp:8071 + uri: http://configServerApp:8080 stream: kafka: binder: @@ -52,7 +54,7 @@ eureka: fetch-registry: true register-with-eureka: true serviceUrl: - defaultZone: http://${eureka.instance.hostname}:8070/eureka/ + defaultZone: http://${eureka.instance.hostname}:8080/eureka/ healthcheck: enabled: true logging: diff --git a/user-service/src/main/resources/application.yml b/user-service/src/main/resources/application.yml index 1c97540..0301df9 100644 --- a/user-service/src/main/resources/application.yml +++ b/user-service/src/main/resources/application.yml @@ -2,11 +2,11 @@ spring: application: name: "users" config: - import: "configserver:http://configServerApp:8071/" + import: "configserver:http://configServerApp:8080/" cloud: config: enabled: true - uri: http://configServerApp:8071 + uri: http://configServerApp:8080 stream: kafka: binder: @@ -19,9 +19,6 @@ spring: env: enabled: true -server: - port: 8080 - management: endpoints: web: @@ -41,7 +38,7 @@ eureka: fetch-registry: true register-with-eureka: true serviceUrl: - defaultZone: http://${eureka.instance.hostname}:8070/eureka/ + defaultZone: http://${eureka.instance.hostname}:8080/eureka/ healthcheck: enabled: true endpoints: