Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions openbas-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
<bcpg-jdk18on.version>1.80</bcpg-jdk18on.version>
<commons-collections4.version>4.5.0</commons-collections4.version>
<commons-email.version>1.5</commons-email.version>
<opensaml.version>4.3.2</opensaml.version>
<springdoc.version>2.6.0</springdoc.version>
<springdoc-plugin.version>1.4</springdoc-plugin.version>
<cron-utils.version>9.2.1</cron-utils.version>
Expand Down Expand Up @@ -179,21 +178,6 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-saml2-service-provider</artifactId>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-impl</artifactId>
<version>${opensaml.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
Expand Down
199 changes: 12 additions & 187 deletions openbas-api/src/main/java/io/openbas/config/AppSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,42 +1,35 @@
package io.openbas.config;

import static io.openbas.database.model.User.ROLE_ADMIN;
import static io.openbas.database.model.User.ROLE_USER;
import static java.util.Optional.ofNullable;
import static io.openbas.config.security.SecurityService.OPENBAS_PROVIDER_PATH_PREFIX;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.springframework.util.StringUtils.hasLength;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import io.openbas.config.security.OpenSamlConfig;
import io.openbas.config.security.SecurityService;
import io.openbas.database.model.User;
import io.openbas.database.repository.UserRepository;
import io.openbas.rest.user.form.user.CreateUserInput;
import io.openbas.security.SsoRefererAuthenticationFailureHandler;
import io.openbas.security.SsoRefererAuthenticationSuccessHandler;
import io.openbas.security.TokenAuthenticationFilter;
import io.openbas.service.UserService;
import jakarta.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
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.registration.ClientRegistration;
Expand All @@ -48,56 +41,24 @@
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.saml2.core.Saml2Error;
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class AppSecurityConfig {

private static final Logger LOGGER = Logger.getLogger(AppSecurityConfig.class.getName());

private UserRepository userRepository;
private UserService userService;
private OpenBASConfig openBASConfig;
private Environment env;
private final Environment env;
private final OpenBASConfig openBASConfig;
private final OpenSamlConfig openSamlConfig;
private final SecurityService securityService;

@Resource protected ObjectMapper mapper;

@Autowired
public void setEnv(Environment env) {
this.env = env;
}

@Autowired
public void setOpenBASConfig(OpenBASConfig openBASConfig) {
this.openBASConfig = openBASConfig;
}

@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}

@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Autowired(required = false)
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
Expand Down Expand Up @@ -146,19 +107,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
}

if (openBASConfig.isAuthSaml2Enable()) {
DefaultRelyingPartyRegistrationResolver relyingPartyRegistrationResolver =
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository);
Saml2MetadataFilter filter =
new Saml2MetadataFilter(relyingPartyRegistrationResolver, new OpenSamlMetadataResolver());

OpenSaml4AuthenticationProvider authenticationProvider = getOpenSaml4AuthenticationProvider();

http.addFilterBefore(filter, Saml2WebSsoAuthenticationFilter.class)
.saml2Login(
saml2Login ->
saml2Login
.authenticationManager(new ProviderManager(authenticationProvider))
.successHandler(new SsoRefererAuthenticationSuccessHandler()));
this.openSamlConfig.addOpenSamlConfig(http);
}

// Rewrite 403 code to 401
Expand All @@ -179,7 +128,7 @@ public TokenAuthenticationFilter tokenAuthenticationFilter() {
private List<String> extractRolesFromToken(OAuth2AccessToken accessToken, String registrationId) {
ObjectReader listReader = mapper.readerFor(new TypeReference<List<String>>() {});
if (accessToken != null) {
String rolesPathConfig = "openbas.provider." + registrationId + ".roles_path";
String rolesPathConfig = OPENBAS_PROVIDER_PATH_PREFIX + registrationId + ".roles_path";
//noinspection unchecked
List<String> rolesPath =
env.getProperty(rolesPathConfig, List.class, new ArrayList<String>());
Expand Down Expand Up @@ -208,70 +157,6 @@ private List<String> extractRolesFromToken(OAuth2AccessToken accessToken, String
return new ArrayList<>();
}

private List<String> extractRolesFromUser(
Saml2AuthenticatedPrincipal user, String registrationId) {
String rolesPathConfig = "openbas.provider." + registrationId + ".roles_path";
//noinspection unchecked
List<String> rolesPath = env.getProperty(rolesPathConfig, List.class, new ArrayList<String>());
try {
return rolesPath.stream()
.flatMap(
path -> {
try {
List<String> roles = user.getAttribute(path);
assert roles != null;
return roles.stream();
} catch (NullPointerException e) {
return Stream.empty();
}
})
.toList();
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
return new ArrayList<>();
}

public User userManagement(
String emailAttribute,
String registrationId,
List<String> rolesFromToken,
String firstName,
String lastName) {
String email = ofNullable(emailAttribute).orElseThrow();
String rolesAdminConfig = "openbas.provider." + registrationId + ".roles_admin";
String allAdminConfig = "openbas.provider." + registrationId + ".all_admin";
//noinspection unchecked
List<String> rolesAdmin =
this.env.getProperty(rolesAdminConfig, List.class, new ArrayList<String>());
boolean allAdmin = this.env.getProperty(allAdminConfig, Boolean.class, false);
boolean isAdmin = allAdmin || rolesAdmin.stream().anyMatch(rolesFromToken::contains);
if (hasLength(email)) {
Optional<User> optionalUser = userRepository.findByEmailIgnoreCase(email);
// If user not exists, create it
if (optionalUser.isEmpty()) {
CreateUserInput createUserInput = new CreateUserInput();
createUserInput.setEmail(email);
createUserInput.setFirstname(firstName);
createUserInput.setLastname(lastName);
if (allAdmin || !rolesAdmin.isEmpty()) {
createUserInput.setAdmin(isAdmin);
}
return this.userService.createUser(createUserInput, 0);
} else {
// If user exists, update it
User currentUser = optionalUser.get();
currentUser.setFirstname(firstName);
currentUser.setLastname(lastName);
if (allAdmin || !rolesAdmin.isEmpty()) {
currentUser.setAdmin(isAdmin);
}
return this.userService.updateUser(currentUser);
}
}
return null;
}

public User userOauth2Management(
OAuth2AccessToken accessToken, ClientRegistration clientRegistration, OAuth2User user) {
String emailAttribute = user.getAttribute("email");
Expand All @@ -286,7 +171,7 @@ public User userOauth2Management(
throw new OAuth2AuthenticationException(authError);
}
User userLogin =
userManagement(
this.securityService.userManagement(
emailAttribute,
registrationId,
rolesFromToken,
Expand All @@ -301,39 +186,6 @@ public User userOauth2Management(
throw new OAuth2AuthenticationException(authError);
}

public User userSaml2Management(Saml2AuthenticatedPrincipal user) {
String emailAttribute = user.getName();
String registrationId = user.getRelyingPartyRegistrationId();
List<String> rolesFromUser = extractRolesFromUser(user, registrationId);
User userLogin = null;
try {
userLogin =
userManagement(
emailAttribute,
registrationId,
rolesFromUser,
user.getFirstAttribute(
env.getProperty(
"openbas.provider." + registrationId + ".firstname_attribute_key",
String.class,
"")),
user.getFirstAttribute(
env.getProperty(
"openbas.provider." + registrationId + ".lastname_attribute_key",
String.class,
"")));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}

if (userLogin != null) {
return userLogin;
}

Saml2Error authError = new Saml2Error("invalid_token", "User conversion fail");
throw new Saml2AuthenticationException(authError);
}

public OidcUser oidcUserManagement(
OAuth2AccessToken accessToken, ClientRegistration clientRegistration, OAuth2User user) {
User loginUser = userOauth2Management(accessToken, clientRegistration, user);
Expand All @@ -346,20 +198,6 @@ public OAuth2User oAuth2UserManagement(
return new OpenBASOAuth2User(loginUser);
}

public Saml2Authentication saml2UserManagement(Saml2Authentication authentication) {
Saml2AuthenticatedPrincipal user = (Saml2AuthenticatedPrincipal) authentication.getPrincipal();
User loginUser = userSaml2Management(user);

List<SimpleGrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority(ROLE_USER));
if (loginUser.isAdmin()) {
roles.add(new SimpleGrantedAuthority(ROLE_ADMIN));
}

return new Saml2Authentication(
new OpenBASSaml2User(loginUser, roles), authentication.getSaml2Response(), roles);
}

@Bean
public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
OidcUserService delegate = new OidcUserService();
Expand All @@ -375,17 +213,4 @@ public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
oAuth2UserManagement(
request.getAccessToken(), request.getClientRegistration(), delegate.loadUser(request));
}

private OpenSaml4AuthenticationProvider getOpenSaml4AuthenticationProvider() {
OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();
authenticationProvider.setResponseAuthenticationConverter(
responseToken -> {
Saml2Authentication authentication =
OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter()
.convert(responseToken);
assert authentication != null;
return saml2UserManagement(authentication);
});
return authenticationProvider;
}
}
Loading