- Integrated entra id into "prod"-profile
This commit is contained in:
parent
f6160a4153
commit
10687ffe5d
8 changed files with 207 additions and 28 deletions
4
pom.xml
4
pom.xml
|
|
@ -60,6 +60,10 @@
|
|||
<groupId>com.azure.spring</groupId>
|
||||
<artifactId>spring-cloud-azure-starter-jdbc-mysql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
|
|
|
|||
57
src/main/java/de/avatic/lcc/config/CorsConfig.java
Normal file
57
src/main/java/de/avatic/lcc/config/CorsConfig.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package de.avatic.lcc.config;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@Profile("dev | test")
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Value("${lcc.allowed_cors}")
|
||||
private String allowedCors;
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(@NotNull CorsRegistry registry) {
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
|
||||
System.out.println("Active profiles: " + Arrays.toString(activeProfiles));
|
||||
System.out.println("Allowed CORS: " + allowedCors);
|
||||
|
||||
if (Arrays.asList(activeProfiles).contains("dev")) {
|
||||
|
||||
System.out.println("Applying DEV CORS configuration");
|
||||
|
||||
// Development CORS configuration
|
||||
registry.addMapping("/api/**")
|
||||
.allowedOriginPatterns("http://localhost:*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
|
||||
.allowCredentials(false);
|
||||
} else {
|
||||
// Production CORS configuration
|
||||
registry.addMapping("/api/**")
|
||||
.allowedOrigins(allowedCors)
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.exposedHeaders("X-Total-Count", "X-Page-Count", "X-Current-Page")
|
||||
.allowCredentials(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package de.avatic.lcc.config;
|
|||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
|
|
@ -10,6 +11,7 @@ import org.springframework.web.servlet.resource.PathResourceResolver;
|
|||
|
||||
import java.io.IOException;
|
||||
@Configuration
|
||||
@Profile("!dev & !test")
|
||||
public class FrontendConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
27
src/main/java/de/avatic/lcc/config/LccOidcUser.java
Normal file
27
src/main/java/de/avatic/lcc/config/LccOidcUser.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package de.avatic.lcc.config;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
|
||||
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class LccOidcUser extends DefaultOidcUser {
|
||||
|
||||
private final Integer userId;
|
||||
|
||||
public LccOidcUser(Collection<? extends GrantedAuthority> authorities,
|
||||
OidcIdToken idToken,
|
||||
OidcUserInfo userInfo,
|
||||
String nameAttributeKey,
|
||||
Integer userId) {
|
||||
super(authorities, idToken, userInfo, nameAttributeKey);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Integer getSqlUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,26 +1,39 @@
|
|||
package de.avatic.lcc.config;
|
||||
|
||||
import de.avatic.lcc.model.users.User;
|
||||
import de.avatic.lcc.repositories.users.GroupRepository;
|
||||
import de.avatic.lcc.repositories.users.UserRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
|
||||
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
|
||||
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
|
||||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfig {
|
||||
|
||||
|
||||
@Bean
|
||||
@Profile("!dev & !test") // Only active when NOT in dev profile
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// Your production security configuration
|
||||
http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.oauth2Login(oauth2 -> oauth2
|
||||
.defaultSuccessUrl("/", true)
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
|
|
@ -32,4 +45,54 @@ public class SecurityConfig {
|
|||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Profile("!dev & !test")
|
||||
public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService(UserRepository userRepository, GroupRepository groupRepository) {
|
||||
final OidcUserService delegate = new OidcUserService();
|
||||
|
||||
return (userRequest) -> {
|
||||
OidcUser oidcUser = delegate.loadUser(userRequest);
|
||||
Integer userId = null;
|
||||
|
||||
// // Debug: Print all claims
|
||||
// System.out.println("=== ID Token Claims ===");
|
||||
// oidcUser.getIdToken().getClaims().forEach((key, value) ->
|
||||
// System.out.println(key + ": " + value)
|
||||
// );
|
||||
// System.out.println("======================");
|
||||
|
||||
Set<GrantedAuthority> mappedAuthorities = new HashSet<>(oidcUser.getAuthorities());
|
||||
|
||||
// Try different ways to get email
|
||||
String email = oidcUser.getEmail();
|
||||
if (email == null) {
|
||||
email = oidcUser.getAttribute("email");
|
||||
}
|
||||
if (email == null) {
|
||||
email = oidcUser.getAttribute("preferred_username");
|
||||
}
|
||||
if (email == null) {
|
||||
email = oidcUser.getAttribute("upn");
|
||||
}
|
||||
|
||||
if (email != null) {
|
||||
User user = userRepository.getByEmail(email);
|
||||
if (user != null) {
|
||||
user.getGroups().forEach(group -> mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + group.getName())));
|
||||
userId = user.getId();
|
||||
} else {
|
||||
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_default"));
|
||||
}
|
||||
}
|
||||
|
||||
return new LccOidcUser(
|
||||
mappedAuthorities,
|
||||
oidcUser.getIdToken(),
|
||||
oidcUser.getUserInfo(),
|
||||
"preferred_username",
|
||||
userId
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import de.avatic.lcc.model.users.User;
|
|||
import de.avatic.lcc.repositories.pagination.SearchQueryPagination;
|
||||
import de.avatic.lcc.repositories.pagination.SearchQueryResult;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowCallbackHandler;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
|
|
@ -19,7 +18,6 @@ import java.sql.Statement;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Repository
|
||||
public class UserRepository {
|
||||
|
|
@ -86,8 +84,7 @@ public class UserRepository {
|
|||
}, keyHolder);
|
||||
|
||||
userId = Objects.requireNonNull(keyHolder.getKey()).intValue();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
String query = """
|
||||
UPDATE sys_user SET email = ?, firstname = ?, lastname = ?, workday_id = ?, is_active = ? WHERE id = ?""";
|
||||
|
||||
|
|
@ -159,7 +156,22 @@ public class UserRepository {
|
|||
FROM sys_user
|
||||
WHERE id = ?""";
|
||||
|
||||
return jdbcTemplate.queryForObject(query, new UserMapper(), id);
|
||||
|
||||
var user = jdbcTemplate.query(query, new UserMapper(), id);
|
||||
|
||||
return user.isEmpty() ? null : user.getFirst();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public User getByEmail(String email) {
|
||||
String query = """
|
||||
SELECT *
|
||||
FROM sys_user
|
||||
WHERE email = ?""";
|
||||
|
||||
var user = jdbcTemplate.query(query, new UserMapper(), email);
|
||||
|
||||
return user.isEmpty() ? null : user.getFirst();
|
||||
}
|
||||
|
||||
private class UserMapper implements RowMapper<User> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package de.avatic.lcc.service.access;
|
||||
|
||||
import de.avatic.lcc.config.LccOidcUser;
|
||||
import de.avatic.lcc.dto.calculation.CalculationStatus;
|
||||
import de.avatic.lcc.dto.calculation.PremiseDTO;
|
||||
import de.avatic.lcc.dto.calculation.edit.PremiseDetailDTO;
|
||||
|
|
@ -27,6 +28,9 @@ import de.avatic.lcc.service.transformer.premise.PremiseTransformer;
|
|||
import de.avatic.lcc.util.exception.base.InternalErrorException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
|
@ -84,6 +88,14 @@ public class PremisesService {
|
|||
//TODO use actual user.
|
||||
userId = 1;
|
||||
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
//todo make a service. and simulate user rights in dev profile.
|
||||
if (authentication != null && authentication.getPrincipal() instanceof LccOidcUser) {
|
||||
LccOidcUser oidcUser = (LccOidcUser) authentication.getPrincipal();
|
||||
}
|
||||
|
||||
|
||||
return SearchQueryResult.map(premiseRepository.listPremises(filter, new SearchQueryPagination(page, limit), userId, deleted, archived, done), admin ? premiseTransformer::toPremiseDTOWithUserInfo : premiseTransformer::toPremiseDTO);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ spring.datasource.username=${DB_USER}
|
|||
spring.datasource.password=${DB_PASSWORD}
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
spring.sql.init.mode=never
|
||||
#spring.profiles.active=setup
|
||||
lcc.bulk.sheet_password=secretSheet?!
|
||||
lcc.allowed_cors=${ALLOWED_CORS_DOMAIN}
|
||||
azure.maps.subscription.key=${AZURE_MAPS_SUBSCRIPTION_KEY}
|
||||
|
|
@ -13,5 +12,8 @@ azure.maps.client.id=your-app-registration-client-id
|
|||
azure.maps.resource.id=/subscriptions/sub-id/resourceGroups/rg-name/providers/Microsoft.Maps/accounts/account-name
|
||||
spring.servlet.multipart.max-file-size=30MB
|
||||
spring.servlet.multipart.max-request-size=50MB
|
||||
spring.web.resources.add-mappings=true
|
||||
|
||||
spring.cloud.azure.active-directory.enabled=true
|
||||
spring.cloud.azure.active-directory.profile.tenant-id=${AZURE_TENANT_ID}
|
||||
spring.cloud.azure.active-directory.credential.client-id=${AZURE_CLIENT_ID}
|
||||
spring.cloud.azure.active-directory.credential.client-secret=${AZURE_CLIENT_SECRET}
|
||||
spring.cloud.azure.active-directory.authorization-clients.graph.scopes=openid,profile,email,https://graph.microsoft.com/User.Read
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue