SecurityConfig.java

1
package edu.ucsb.cs156.gauchoride.config;
2
3
import java.io.IOException;
4
import java.util.ArrayList;
5
import java.util.HashSet;
6
import java.util.List;
7
import java.util.Map;
8
import java.util.Optional;
9
import java.util.Set;
10
import java.util.function.Supplier;
11
12
import edu.ucsb.cs156.gauchoride.entities.User;
13
import edu.ucsb.cs156.gauchoride.repositories.UserRepository;
14
15
import jakarta.servlet.FilterChain;
16
import jakarta.servlet.ServletException;
17
import jakarta.servlet.http.HttpServletRequest;
18
import jakarta.servlet.http.HttpServletResponse;
19
import lombok.extern.slf4j.Slf4j;
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.beans.factory.annotation.Value;
22
import org.springframework.context.annotation.Bean;
23
import org.springframework.context.annotation.Configuration;
24
import org.springframework.security.config.Customizer;
25
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
26
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
27
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
28
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
29
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
30
import org.springframework.security.config.http.SessionCreationPolicy;
31
import org.springframework.security.core.Authentication;
32
import org.springframework.security.core.GrantedAuthority;
33
import org.springframework.security.core.authority.SimpleGrantedAuthority;
34
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
35
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
36
import org.springframework.security.web.SecurityFilterChain;
37
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
38
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
39
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
40
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
41
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
42
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
43
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
44
import org.springframework.security.web.csrf.CsrfToken;
45
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
46
import org.springframework.util.StringUtils;
47
import org.springframework.web.filter.OncePerRequestFilter;
48
49
@Configuration
50
@EnableWebSecurity
51
@EnableMethodSecurity
52
@Slf4j
53
public class SecurityConfig {
54
55
  @Value("${app.admin.emails}")
56
  private final List<String> adminEmails = new ArrayList<String>();
57
58
  @Autowired
59
  UserRepository userRepository;
60
  
61
  @Bean
62
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
63
    http
64
        .exceptionHandling(handling -> handling.authenticationEntryPoint(new Http403ForbiddenEntryPoint()))
65
        .oauth2Login(
66
            oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userAuthoritiesMapper(this.userAuthoritiesMapper())))
67
        .csrf(csrf -> csrf
68
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
69
            .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()))
70
        .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class)
71
        .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
72
        .logout(logout -> logout.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/"));
73
    return http.build();
74
  }
75
76
  @Bean
77
  public WebSecurityCustomizer webSecurityCustomizer() {
78
    return web -> web.ignoring().requestMatchers("/h2-console/**");
79
  }
80
81
  private GrantedAuthoritiesMapper userAuthoritiesMapper() {
82
    return (authorities) -> {
83
      Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
84
85
      authorities.forEach(authority -> {
86
        log.info("********** authority={}", authority);
87
        mappedAuthorities.add(authority);
88
        if (authority instanceof OAuth2UserAuthority oauth2UserAuthority) {
89
90
          Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
91
          log.info("********** userAttributes={}", userAttributes);
92
93
          mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
94
95
          String email = (String) userAttributes.get("email");
96
          if (getAdmin(email)) {
97
            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
98
          }
99
100
101
          if (getDriver(email)) {
102
            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_DRIVER"));
103
          }
104
105
          if (getRider(email)) {
106
            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_RIDER"));
107
          }
108
109
          if (email.endsWith("@ucsb.edu")) {
110
            mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_MEMBER"));
111
          }
112
        }
113
114
      });
115
      return mappedAuthorities;
116
    };
117
  }
118
119
  public boolean getAdmin(String email) {
120
    if (adminEmails.contains(email)) {
121
      return true;
122
    }
123
    Optional<User> u = userRepository.findByEmail(email);
124
    return u.isPresent() && u.get().getAdmin();
125
  }
126
127
128
  public boolean getDriver(String email){
129
    Optional<User> u = userRepository.findByEmail(email);
130
    return u.isPresent() && u.get().getDriver();
131
  }
132
133
  public boolean getRider(String email){
134
    Optional<User> u = userRepository.findByEmail(email);
135
    return u.isPresent() && u.get().getRider();
136
  }
137
138
  final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
139
    private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
140
  
141
    @Override
142
    public void handle(HttpServletRequest request, HttpServletResponse response,
143
        Supplier<CsrfToken> deferredCsrfToken) {
144
      /*
145
       * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection
146
       * of
147
       * the CsrfToken when it is rendered in the response body.
148
       */
149 1 1. handle : removed call to org/springframework/security/web/csrf/CsrfTokenRequestHandler::handle → KILLED
      this.delegate.handle(request, response, deferredCsrfToken);
150
    }
151
  
152
    @Override
153
    public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
154
      /*
155
       * If the request contains a request header, use
156
       * CsrfTokenRequestAttributeHandler
157
       * to resolve the CsrfToken. This applies when a single-page application
158
       * includes
159
       * the header value automatically, which was obtained via a cookie containing
160
       * the
161
       * raw CsrfToken.
162
       */
163 1 1. resolveCsrfTokenValue : negated conditional → KILLED
      if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
164 1 1. resolveCsrfTokenValue : replaced return value with "" for edu/ucsb/cs156/gauchoride/config/SecurityConfig$SpaCsrfTokenRequestHandler::resolveCsrfTokenValue → KILLED
        return super.resolveCsrfTokenValue(request, csrfToken);
165
      }
166
      /*
167
       * In all other cases (e.g. if the request contains a request parameter), use
168
       * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
169
       * when a server-side rendered form includes the _csrf request parameter as a
170
       * hidden input.
171
       */
172 1 1. resolveCsrfTokenValue : replaced return value with "" for edu/ucsb/cs156/gauchoride/config/SecurityConfig$SpaCsrfTokenRequestHandler::resolveCsrfTokenValue → KILLED
      return this.delegate.resolveCsrfTokenValue(request, csrfToken);
173
    }
174
  }
175
  
176
  final class CsrfCookieFilter extends OncePerRequestFilter {
177
  
178
    @Override
179
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
180
        throws ServletException, IOException {
181
      CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
182
      // Render the token value to a cookie by causing the deferred token to be loaded
183
      csrfToken.getToken();
184 1 1. doFilterInternal : removed call to jakarta/servlet/FilterChain::doFilter → KILLED
      filterChain.doFilter(request, response);
185
    }
186
  }
187
  
188
}
189

Mutations

149

1.1
Location : handle
Killed by : edu.ucsb.cs156.gauchoride.controllers.DriversControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.gauchoride.controllers.DriversControllerTests]/[method:users__logged_out_all()]
removed call to org/springframework/security/web/csrf/CsrfTokenRequestHandler::handle → KILLED

163

1.1
Location : resolveCsrfTokenValue
Killed by : edu.ucsb.cs156.gauchoride.controllers.UsersControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.gauchoride.controllers.UsersControllerTests]/[method:admin_tries_to_toggle_rider_for_non_existant_user_and_gets_right_error_message()]
negated conditional → KILLED

164

1.1
Location : resolveCsrfTokenValue
Killed by : edu.ucsb.cs156.gauchoride.config.SecurityConfigTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.gauchoride.config.SecurityConfigTests]/[method:test_SpaCsrfTokenRequestHandler()]
replaced return value with "" for edu/ucsb/cs156/gauchoride/config/SecurityConfig$SpaCsrfTokenRequestHandler::resolveCsrfTokenValue → KILLED

172

1.1
Location : resolveCsrfTokenValue
Killed by : edu.ucsb.cs156.gauchoride.controllers.UsersControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.gauchoride.controllers.UsersControllerTests]/[method:admin_tries_to_toggle_rider_for_non_existant_user_and_gets_right_error_message()]
replaced return value with "" for edu/ucsb/cs156/gauchoride/config/SecurityConfig$SpaCsrfTokenRequestHandler::resolveCsrfTokenValue → KILLED

184

1.1
Location : doFilterInternal
Killed by : edu.ucsb.cs156.gauchoride.controllers.DriversControllerTests.[engine:junit-jupiter]/[class:edu.ucsb.cs156.gauchoride.controllers.DriversControllerTests]/[method:users__logged_out_all()]
removed call to jakarta/servlet/FilterChain::doFilter → KILLED

Active mutators

Tests examined


Report generated by PIT 1.7.3