Enhanced CORS configuration for OAuth2 and role-based tab visibility in frontend:

- **Backend**: Added separate CORS settings for `/oauth2/token` endpoint with enhanced origin handling based on new `lcc.allowed_oauth_token_cors` property.
- **Frontend**: Updated `Config.vue` to conditionally display `nodesTab` and `bulkOperationsTab` based on user roles.
This commit is contained in:
Jan 2025-10-30 15:05:12 +01:00
parent e1791942cb
commit a289cce805
3 changed files with 77 additions and 48 deletions

View file

@ -90,12 +90,16 @@ export default {
tabs.push(this.materialsTab); tabs.push(this.materialsTab);
} }
if (this.activeUserStore.isSuper || this.activeUserStore.isMaterial || this.activeUserStore.isPackaging || this.activeUserStore.isFreight) {
tabs.push(this.nodesTab); tabs.push(this.nodesTab);
}
if (this.activeUserStore.allowRates) if (this.activeUserStore.allowRates)
tabs.push(this.ratesTab); tabs.push(this.ratesTab);
if (this.activeUserStore.isSuper || this.activeUserStore.isMaterial || this.activeUserStore.isPackaging || this.activeUserStore.isFreight) {
tabs.push(this.bulkOperationsTab); tabs.push(this.bulkOperationsTab);
}
return tabs; return tabs;

View file

@ -57,9 +57,12 @@ public class SecurityConfig {
@Value("${lcc.allowed_cors}") @Value("${lcc.allowed_cors}")
private String allowedCors; private String allowedCors;
@Value("${lcc.allowed_oauth_token_cors:*}") // Default: alle Origins
private String oauthTokenCors;
@Bean @Bean
@Profile("!dev & !test") // Only active when NOT in dev profile @Profile("!dev & !test") // Only active when NOT in dev profile
public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtTokenService jwtTokenService) throws Exception { public SecurityFilterChain prodSecurityFilterChain(HttpSecurity http, JwtTokenService jwtTokenService) throws Exception {
http http
.cors(cors -> cors.configurationSource(prodCorsConfigurationSource())) // Production CORS .cors(cors -> cors.configurationSource(prodCorsConfigurationSource())) // Production CORS
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
@ -98,30 +101,31 @@ public class SecurityConfig {
return http.build(); return http.build();
} }
@Bean
@Profile("dev | test")
public CorsConfigurationSource devCorsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(List.of("http://localhost:*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "X-Page-Count", "X-Current-Page"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
// Production CORS Configuration // Production CORS Configuration
@Bean @Bean
@Profile("!dev & !test") @Profile("!dev & !test")
public CorsConfigurationSource prodCorsConfigurationSource() { public CorsConfigurationSource prodCorsConfigurationSource() {
// CORS for /oauth2/token
CorsConfiguration tokenConfiguration = new CorsConfiguration();
if ("*".equals(oauthTokenCors)) {
tokenConfiguration.setAllowedOriginPatterns(List.of("*"));
} else {
String[] tokenOrigins = oauthTokenCors.split(",");
for (int i = 0; i < tokenOrigins.length; i++) {
tokenOrigins[i] = tokenOrigins[i].trim();
}
if (tokenOrigins.length != 0) {
tokenConfiguration.setAllowedOrigins(Arrays.asList(tokenOrigins));
}
}
CorsConfiguration configuration = new CorsConfiguration(); CorsConfiguration configuration = new CorsConfiguration();
if ("*".equals(allowedCors)) {
configuration.setAllowedOriginPatterns(List.of("*"));
} else {
// Parse comma-separated origins from property // Parse comma-separated origins from property
String[] origins = allowedCors.split(","); String[] origins = allowedCors.split(",");
for (int i = 0; i < origins.length; i++) { for (int i = 0; i < origins.length; i++) {
@ -131,6 +135,8 @@ public class SecurityConfig {
if (origins.length != 0) { if (origins.length != 0) {
configuration.setAllowedOrigins(Arrays.asList(origins)); configuration.setAllowedOrigins(Arrays.asList(origins));
} }
}
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*")); configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true); configuration.setAllowCredentials(true);
@ -138,30 +144,12 @@ public class SecurityConfig {
configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "X-Page-Count", "X-Current-Page")); configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "X-Page-Count", "X-Current-Page"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration); source.registerCorsConfiguration("/**", configuration);
source.registerCorsConfiguration("/oauth2/token", tokenConfiguration);
return source; return source;
} }
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
// Für Entra ID Tokens
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("scp");
converter.setAuthorityPrefix("SCOPE_");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean @Bean
@Profile("dev | test") @Profile("dev | test")
public SecurityFilterChain devSecurityFilterChain(HttpSecurity http, UserRepository userRepository, JwtTokenService jwtTokenService) throws Exception { public SecurityFilterChain devSecurityFilterChain(HttpSecurity http, UserRepository userRepository, JwtTokenService jwtTokenService) throws Exception {
@ -188,6 +176,42 @@ public class SecurityConfig {
.build(); .build();
} }
@Bean
@Profile("dev | test")
public CorsConfigurationSource devCorsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(List.of("http://localhost:*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Arrays.asList("X-Total-Count", "X-Page-Count", "X-Current-Page"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
// Für Entra ID Tokens
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("scp");
converter.setAuthorityPrefix("SCOPE_");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean @Bean
@Profile("!dev & !test") @Profile("!dev & !test")
public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService(UserRepository userRepository, GroupRepository groupRepository) { public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService(UserRepository userRepository, GroupRepository groupRepository) {

View file

@ -23,3 +23,4 @@ spring.flyway.baseline-on-migrate=true
spring.sql.init.mode=never spring.sql.init.mode=never
lcc.allowed_cors= lcc.allowed_cors=
lcc.allowed_oauth_token_cors=*