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:
parent
e1791942cb
commit
a289cce805
3 changed files with 77 additions and 48 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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=*
|
||||||
Loading…
Add table
Reference in a new issue