Skip to content

Commit

Permalink
Improve ootb user experience and fix minor errors
Browse files Browse the repository at this point in the history
Change OotbActiveUserProfileService in the test from autowired to MockBean and add mocking in setup function for better unit test

Ignore post construct in ootb controller in OotbSecurityConfigTest to fucus on the functions of OotbSecurityConfig test

test for v4 git actions

Change post construct function to update admin user at first to EventListener

test building

Modify all the ootb test class with mocking ootb controller to return null when it interacts with api projects in the class

Change coverage use upload-artifact@v2

Change coverage yml with upload-artifact@v4

test remove post construct

test 2

test upload-artifact@v2

change

Fix MockMVC to test end point for ootb rest controller

Change dependency injections between ootb related classes by removing some beans and adding some annotations and local field in the class

Modify the test for the changes in the class and add @activeprofiles annotation to separate the test from localhost env
  • Loading branch information
gitCarrot committed Sep 18, 2024
1 parent 2241543 commit 122c680
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 321 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import edu.hawaii.its.api.service.HttpRequestService;
import edu.hawaii.its.api.service.OotbHttpRequestService;
import edu.hawaii.its.groupings.access.UserContextService;
import edu.hawaii.its.groupings.configuration.OotbStaticUserAuthenticationFilter;
Expand All @@ -28,19 +27,14 @@
@RequestMapping("/api/groupings/ootb")
public class OotbRestController {
private static final Log logger = LogFactory.getLog(OotbRestController.class);
private final PolicyFactory policy = Sanitizers.FORMATTING;;

@Value("${url.api.2.1.base}")
private String API_2_1_BASE;

private final PolicyFactory policy = Sanitizers.FORMATTING;
private final UserContextService userContextService;

private final OotbStaticUserAuthenticationFilter ootbAuthenticationFilter;

private final OotbActiveUserProfileService ootbActiveUserProfileService;

private final OotbHttpRequestService ootbHttpRequestService;

@Value("${url.api.2.1.base}")
private String API_2_1_BASE;

// Constructor.
public OotbRestController(UserContextService userContextService,
Expand All @@ -67,7 +61,6 @@ public ResponseEntity<List<String>> getAvailableProfiles() {
@PostMapping(value = "/{activeProfile}")
public ResponseEntity<OotbActiveProfile> updateActiveDefaultUser(@PathVariable String activeProfile) {
String currentUid = policy.sanitize(userContextService.getCurrentUid());
ootbAuthenticationFilter.setUserProfile(activeProfile);
OotbActiveProfile ootbActiveProfile = ootbActiveUserProfileService.getActiveProfiles().get(activeProfile);
String baseUri = API_2_1_BASE + "/activeProfile/ootb";
return ootbHttpRequestService.makeApiRequestWithActiveProfileBody(currentUid, baseUri, ootbActiveProfile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,50 @@
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

import java.util.List;
import jakarta.annotation.PostConstruct;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.ws.config.annotation.DelegatingWsConfiguration;

import edu.hawaii.its.api.controller.OotbRestController;
import edu.hawaii.its.groupings.access.DelegatingAuthenticationFailureHandler;
import edu.hawaii.its.groupings.service.OotbActiveUserProfileService;

@EnableWebSecurity
@Configuration
@ConditionalOnProperty(name = "grouping.api.server.type", havingValue = "OOTB")
@Profile("ootb")
public class OotbSecurityConfig {

private static final Log logger = LogFactory.getLog(OotbSecurityConfig.class);
private String userProfile;

@Value("${ootb.profiles.filename}")
private String profilesFileName;

private final OotbRestController ootbRestController;
private final OotbStaticUserAuthenticationFilter ootbStaticUserAuthenticationFilter;
private final OotbActiveUserProfileService ootbActiveUserProfileService;
@Value("${url.base}")
private String appUrlBase;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration,
UserDetailsService userDetailsService) throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
return new ProviderManager(List.of(provider));
public OotbSecurityConfig(OotbRestController ootbRestController,
OotbStaticUserAuthenticationFilter ootbStaticUserAuthenticationFilter,
OotbActiveUserProfileService ootbActiveUserProfileService) {
this.ootbRestController = ootbRestController;
this.ootbStaticUserAuthenticationFilter = ootbStaticUserAuthenticationFilter;
this.ootbActiveUserProfileService = ootbActiveUserProfileService;
}

@PostConstruct
public void init() {
updateActiveDefaultUser();
}

@Bean
Expand All @@ -69,7 +67,7 @@ public DelegatingWsConfiguration delegatingWsConfiguration() {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(ootbStaticUserAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(ootbStaticUserAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests((auths)
-> auths
.requestMatchers(antMatcher("/")).permitAll()
Expand Down Expand Up @@ -102,22 +100,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.build();
}

@Bean
public OotbStaticUserAuthenticationFilter ootbStaticUserAuthenticationFilter() {
return new OotbStaticUserAuthenticationFilter(userDetailsService(), userProfile);
}

@Bean
public UserDetailsService userDetailsService() {
// Make Profile Service with JSON file that user sets in the properties file
OotbActiveUserProfileService ootbActiveUserProfileService = new OotbActiveUserProfileService(profilesFileName);

// Set the default profile to admin
setUserProfile(ootbActiveUserProfileService.findGivenNameForAdminRole());
return ootbActiveUserProfileService;
}

public void setUserProfile(String userProfile) {
this.userProfile = userProfile;
public void updateActiveDefaultUser() {
ootbRestController.updateActiveDefaultUser(ootbActiveUserProfileService.findGivenNameForAdminRole());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,37 @@
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

public class OotbStaticUserAuthenticationFilter extends GenericFilterBean {
import edu.hawaii.its.groupings.service.OotbActiveUserProfileService;

private final UserDetailsService userDetailsService;
private volatile String userProfile;
@Component
@Profile("ootb")
public class OotbStaticUserAuthenticationFilter extends GenericFilterBean {

public OotbStaticUserAuthenticationFilter(UserDetailsService userDetailsService, String userProfile) {
this.userProfile = userProfile;
this.userDetailsService = userDetailsService;
}
private final OotbActiveUserProfileService ootbActiveUserProfileService;

public void setUserProfile(String userProfile) {
this.userProfile = userProfile;
public OotbStaticUserAuthenticationFilter(
OotbActiveUserProfileService ootbActiveUserProfileService) {
this.ootbActiveUserProfileService = ootbActiveUserProfileService;
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(userProfile);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetails userDetails = ootbActiveUserProfileService.loadUserByUsername(
ootbActiveUserProfileService.findGivenNameForAdminRole());
if(userDetails != null) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package edu.hawaii.its.groupings.configuration;

import edu.hawaii.its.groupings.access.CasUserDetailsServiceImpl;
import edu.hawaii.its.groupings.access.DelegatingAuthenticationFailureHandler;
import edu.hawaii.its.groupings.access.UserBuilder;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

import jakarta.annotation.PostConstruct;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.apereo.cas.client.session.SingleSignOutFilter;
import org.apereo.cas.client.validation.Saml11TicketValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
Expand All @@ -34,38 +34,32 @@
import org.springframework.util.Assert;
import org.springframework.ws.config.annotation.DelegatingWsConfiguration;

import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
import edu.hawaii.its.groupings.access.CasUserDetailsServiceImpl;
import edu.hawaii.its.groupings.access.DelegatingAuthenticationFailureHandler;
import edu.hawaii.its.groupings.access.UserBuilder;

@EnableWebSecurity
@Configuration
@ConditionalOnProperty(name = "grouping.api.server.type", havingValue = "GROUPER", matchIfMissing = true)
@Profile("!ootb")
public class SecurityConfig {

private static final Log logger = LogFactory.getLog(SecurityConfig.class);

private final UserBuilder userBuilder;
@Value("${url.base}")
private String appUrlBase;

@Value("${app.url.home}")
private String appUrlHome;

@Value("${cas.login.url}")
private String casLoginUrl;

@Value("${cas.logout.url}")
private String casLogoutUrl;

@Value("${cas.main.url}")
private String casMainUrl;

@Value("${cas.saml.tolerance}")
private long casSamlTolerance;

@Value("${cas.send.renew:false}")
private boolean casSendRenew;

private final UserBuilder userBuilder;

public SecurityConfig(UserBuilder userBuilder) {
this.userBuilder = userBuilder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.hawaii.its.groupings.controller;

import java.util.Arrays;
import java.util.Locale;
import java.util.Map;

Expand All @@ -9,6 +10,7 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
Expand All @@ -20,6 +22,7 @@
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import edu.hawaii.its.groupings.access.UserContextService;
import edu.hawaii.its.groupings.configuration.Realm;
import edu.hawaii.its.groupings.service.EmailService;
import edu.hawaii.its.groupings.type.Feedback;

Expand All @@ -38,15 +41,21 @@ public class HomeController {

private final UserContextService userContextService;

public HomeController(EmailService emailService, UserContextService userContextService) {
private final Realm realmService;

public HomeController(EmailService emailService, UserContextService userContextService, Realm realmService) {
this.emailService = emailService;
this.userContextService = userContextService;
this.realmService = realmService;
}

// Mapping to home.
@GetMapping(value = { "/", "/home" })
public String home(Map<String, Locale> locale) {
logger.info("User at home. The client locale is " + locale);
if (realmService.isOotb()) {
return "redirect:/login";
}
return "home";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import java.util.Map;
import java.util.Optional;

import jakarta.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
Expand All @@ -17,26 +21,24 @@
import edu.hawaii.its.groupings.util.JsonUtil;

@Service
@Profile("ootb")
public class OotbActiveUserProfileService implements UserDetailsService {

@Value("${ootb.profiles.filename}")
private String profilesFileName;

// User is for SecurityContextHolder in AuthenticationFilter in UI project
private final Map<String, User> users = new LinkedHashMap<>();

// OotbActiveProfile is for request body to make api request
private final Map<String, OotbActiveProfile> activeProfiles = new LinkedHashMap<>();

// Default JSON file for active profiles
private String profilesFileName = "ootb.active.user.profiles.json";

public OotbActiveUserProfileService() {
initUsers();
}
public OotbActiveUserProfileService() {}

public OotbActiveUserProfileService(String profilesFileName) {
this.profilesFileName = profilesFileName;
initUsers();
}

@PostConstruct
private void initUsers() {
List<OotbActiveProfile> ootbActiveProfiles =
JsonUtil.asList(JsonUtil.readJsonFileToString(profilesFileName), OotbActiveProfile.class);
Expand Down
Loading

0 comments on commit 122c680

Please sign in to comment.