Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: refactor tests with authentication #76

Merged
merged 7 commits into from
Jul 8, 2024
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
25 changes: 25 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<javafaker-version>1.0.2</javafaker-version>
<sonar.organization>inseefr</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<cucumber.version>7.18.0</cucumber.version>
</properties>
<dependencies>
<dependency>
Expand Down Expand Up @@ -101,6 +102,30 @@
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>

<!--Cucumber-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<version>${cucumber.version}</version>
</dependency>



<!-- Fakers for poc data -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package fr.insee.survey.datacollectionmanagement.config;


import fr.insee.survey.datacollectionmanagement.config.auth.user.AuthUser;
import fr.insee.survey.datacollectionmanagement.config.auth.user.UserProvider;
import fr.insee.survey.datacollectionmanagement.constants.AuthConstants;
import fr.insee.survey.datacollectionmanagement.config.auth.user.AuthenticationUserHelper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
Expand All @@ -22,37 +18,25 @@
@RequiredArgsConstructor
public class LogInterceptor implements HandlerInterceptor {

private final ApplicationConfig applicationConfig;
private final AuthenticationUserHelper authenticationUserHelper;

private final UserProvider userProvider;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

String fishTag = UUID.randomUUID().toString();
String method = request.getMethod();
String operationPath = request.getRequestURI();

String userId = null;

switch (applicationConfig.getAuthType()) {
Authentication authentication = authenticationUserHelper.getCurrentUser();
ThreadContext.put("user", authentication.getName().toUpperCase());

case AuthConstants.OIDC:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
AuthUser currentAuthUser = userProvider.getUser(authentication);
userId = (currentAuthUser != null && currentAuthUser.getId() != null ? currentAuthUser.getId() : "anonymous");
ThreadContext.put("user", userId.toUpperCase());
break;
default:
userId = "GUEST";
break;
}

ThreadContext.put("id", fishTag);
ThreadContext.put("path", operationPath);
ThreadContext.put("method", method);


log.info("[" + userId.toUpperCase() + "] - [" + method + "] - [" + operationPath + "]");
log.info("[" + authentication.getName().toUpperCase() + "] - [" + method + "] - [" + operationPath + "]");
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package fr.insee.survey.datacollectionmanagement.config;

import fr.insee.survey.datacollectionmanagement.config.auth.user.UserProvider;
import fr.insee.survey.datacollectionmanagement.config.auth.user.AuthenticationUserHelper;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -11,19 +11,16 @@
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

private final ApplicationConfig applicationConfig;

private final UserProvider userProvider;
private final AuthenticationUserHelper authenticationUserHelper;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.myLogInterceptor());
//.addPathPatterns("api/**");
}

@Bean
public LogInterceptor myLogInterceptor() {
return new LogInterceptor(applicationConfig,userProvider);
return new LogInterceptor(authenticationUserHelper);
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package fr.insee.survey.datacollectionmanagement.config.auth.security;

import fr.insee.survey.datacollectionmanagement.config.ApplicationConfig;
import fr.insee.survey.datacollectionmanagement.config.auth.user.AuthUser;
import fr.insee.survey.datacollectionmanagement.config.auth.user.UserProvider;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
Expand All @@ -16,8 +14,6 @@
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;

import java.util.Collections;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
Expand Down Expand Up @@ -56,11 +52,6 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@Bean
@Order(1)
SecurityFilterChain filterPublicUrlsChain(HttpSecurity http) throws Exception {
return publicSecurityFilterChainConfiguration.buildSecurityPublicFilterChain(http, config.getPublicUrls()); }
@Bean
public UserProvider getUserProvider() {
return auth -> new AuthUser("anonymous", Collections.emptyList());
return publicSecurityFilterChainConfiguration.buildSecurityPublicFilterChain(http, config.getPublicUrls());
}


}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package fr.insee.survey.datacollectionmanagement.config.auth.security;

import fr.insee.survey.datacollectionmanagement.config.ApplicationConfig;
import fr.insee.survey.datacollectionmanagement.config.auth.user.AuthorityRoleEnum;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
Expand All @@ -13,27 +15,39 @@
@AllArgsConstructor
public class GrantedAuthorityConverter implements Converter<Jwt, Collection<GrantedAuthority>> {

private final Map<String, SimpleGrantedAuthority> grantedRoles;
ApplicationConfig applicationConfig;

public GrantedAuthorityConverter(ApplicationConfig applicationConfig) {
this.applicationConfig = applicationConfig;
this.grantedRoles = new HashMap<>();
fillGrantedRoles(applicationConfig.getRoleAdmin(), AuthorityRoleEnum.ADMIN);
fillGrantedRoles(applicationConfig.getRoleRespondent(), AuthorityRoleEnum.RESPONDENT);
fillGrantedRoles(applicationConfig.getRoleInternalUser(), AuthorityRoleEnum.INTERNAL_USER);
fillGrantedRoles(applicationConfig.getRoleWebClient(), AuthorityRoleEnum.WEB_CLIENT);
}

@SuppressWarnings("unchecked")
@Override
public Collection<GrantedAuthority> convert(Jwt jwt) {
public Collection<GrantedAuthority> convert(@NonNull Jwt jwt) {
Map<String, Object> claims = jwt.getClaims();
List<String> roles = (List<String>) claims.get(applicationConfig.getRoleClaim());
List<String> authorizedRoles = new ArrayList<>();
authorizedRoles.addAll(applicationConfig.getRoleAdmin());
authorizedRoles.addAll(applicationConfig.getRoleRespondent());
authorizedRoles.addAll(applicationConfig.getRoleInternalUser());
authorizedRoles.addAll(applicationConfig.getRoleWebClient());

return roles.stream()
.map(role -> {
if (authorizedRoles.contains(role)) {
return new SimpleGrantedAuthority(role);
}
return null;
})
.filter(Objects::nonNull)
.filter(role -> !role.isBlank())
.filter(grantedRoles::containsKey)
.map(grantedRoles::get)
.collect(Collectors.toCollection(ArrayList::new));
}

private void fillGrantedRoles(List<String> listRoles, AuthorityRoleEnum roleEnum) {

for (String role : listRoles ) {
this.grantedRoles.putIfAbsent(role,
new SimpleGrantedAuthority(roleEnum.securityRole()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if same config role for multiple app roles ?

}

}
}

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package fr.insee.survey.datacollectionmanagement.config.auth.security;

import fr.insee.survey.datacollectionmanagement.config.ApplicationConfig;
import fr.insee.survey.datacollectionmanagement.config.auth.user.AuthUser;
import fr.insee.survey.datacollectionmanagement.config.auth.user.UserProvider;
import fr.insee.survey.datacollectionmanagement.constants.AuthConstants;
import fr.insee.survey.datacollectionmanagement.constants.Constants;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,7 +25,6 @@
import org.springframework.security.web.header.writers.XXssProtectionHeaderWriter;

import java.util.Collection;
import java.util.List;

@Configuration
@EnableWebSecurity
Expand Down Expand Up @@ -80,18 +77,7 @@ SecurityFilterChain filterPublicUrlsChain(HttpSecurity http) throws Exception {
String tokenUrl = config.getKeyCloakUrl() + "/realms/" + config.getKeycloakRealm() + "/protocol/openid-connect/token";
String authorizedConnectionHost = config.getAuthType().equals(AuthConstants.OIDC) ?
" " + tokenUrl : "";
return publicSecurityFilterChainConfiguration.buildSecurityPublicFilterChain(http, config.getPublicUrls(), authorizedConnectionHost); }

@Bean
public UserProvider getUserProvider() {
return auth -> {
if ("anonymousUser".equals(auth.getPrincipal()))
return null; //init request, or request without authentication
final Jwt jwt = (Jwt) auth.getPrincipal();
List<String> tryRoles = jwt.getClaimAsStringList(config.getRoleClaim());
String tryId = jwt.getClaimAsString(config.getIdClaim());
return new AuthUser(tryId, tryRoles);
};
return publicSecurityFilterChainConfiguration.buildSecurityPublicFilterChain(http, config.getPublicUrls(), authorizedConnectionHost);
}

@Bean
Expand All @@ -105,7 +91,4 @@ JwtAuthenticationConverter jwtAuthenticationConverter(ApplicationConfig applicat
Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter(ApplicationConfig applicationConfig) {
return new GrantedAuthorityConverter(applicationConfig);
}



}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fr.insee.survey.datacollectionmanagement.config.auth.user;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

@Component
public class AuthenticationUserHelper {

public Authentication getCurrentUser(){
return SecurityContextHolder.getContext().getAuthentication();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package fr.insee.survey.datacollectionmanagement.config.auth.user;

public class AuthorityPrivileges {
private AuthorityPrivileges() {
throw new IllegalArgumentException("Constant class");
}

public static final String HAS_MANAGEMENT_PRIVILEGES = "hasAnyRole('INTERNAL_USER', 'WEB_CLIENT', 'ADMIN')";
public static final String HAS_REPONDENT_PRIVILEGES = "hasAnyRole('RESPONDENT')";
public static final String HAS_REPONDENT_LIMITATED_PRIVILEGES = "hasRole('RESPONDENT') && #id == authentication.name ";
public static final String HAS_ADMIN_PRIVILEGES = "hasAnyRole('WEB_CLIENT', 'ADMIN)";
public static final String HAS_USER_PRIVILEGES = "hasAnyRole('INTERNAL_USER', 'WEB_CLIENT', 'RESPONDENT', 'ADMIN')";


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package fr.insee.survey.datacollectionmanagement.config.auth.user;

public enum AuthorityRoleEnum {

ADMIN,
WEB_CLIENT,
INTERNAL_USER,
RESPONDENT;

public static final String ROLE_PREFIX = "ROLE_";

public String securityRole() {
return ROLE_PREFIX + this.name();
}

}
Loading
Loading