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
18 changes: 0 additions & 18 deletions .github/workflows/github-actions-demo.yml

This file was deleted.

10 changes: 4 additions & 6 deletions projekt/backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0</version>
<version>4.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.projektSSE</groupId>
Expand Down Expand Up @@ -41,7 +41,7 @@
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>7.0.0</version>
<version>7.0.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
Expand Down Expand Up @@ -81,14 +81,12 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>4.0.1</version>
</dependency>
<!-- Test Dependencies -->
<!-- Source: https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-resttestclient -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-resttestclient</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
Expand All @@ -100,15 +98,15 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.22.0</version>
<version>5.23.0</version>
<scope>test</scope>
</dependency>
<!-- Logging Dependencies -->
<!-- Source: https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.1.0-alpha1</version>
<version>2.0.17</version>
<scope>compile</scope>
</dependency>
<!-- Devtools Dependencies -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.projektsse.backend.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.NonNull;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import tools.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.net.URI;

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

private final ObjectMapper objectMapper;

public CustomAccessDeniedHandler(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@Override
public void handle(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull AccessDeniedException accessDeniedException) throws IOException {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.FORBIDDEN, "Forbidden");
problemDetail.setDetail("You do not have permission to access this resource.");
problemDetail.setInstance(URI.create(request.getRequestURI()));

response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType(MediaType.APPLICATION_PROBLEM_JSON_VALUE);
objectMapper.writeValue(response.getWriter(), problemDetail);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.projektsse.backend.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.NonNull;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import tools.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.net.URI;

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final ObjectMapper objectMapper;

public CustomAuthenticationEntryPoint(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}


@Override
public void commence(@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull AuthenticationException authException) throws IOException {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, "Unauthorized");
problemDetail.setDetail("Authentication is required to access this resource.");
problemDetail.setInstance(URI.create(request.getRequestURI()));

response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_PROBLEM_JSON_VALUE);
objectMapper.writeValue(response.getWriter(), problemDetail);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.projektsse.backend.config;

import com.projektsse.backend.service.JwtService;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.NonNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
Expand All @@ -21,7 +23,9 @@
@Component
public class JwtFilter extends OncePerRequestFilter {

private static final String FINGERPRINT_COOKIE_NAME = "__Secure-Fgp";
@Value("${app.cookie.secure}")
private boolean cookieSecure;
private String FINGERPRINT_COOKIE_NAME;

private final JwtService jwtService;
private final UserDetailsService userDetailsService;
Expand All @@ -31,6 +35,11 @@ public JwtFilter(JwtService jwtService, UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

@PostConstruct
public void init() {
FINGERPRINT_COOKIE_NAME = cookieSecure ? "__Secure-Fgp" : "Fgp";
}

@Override
protected void doFilterInternal(
HttpServletRequest request,
Expand Down Expand Up @@ -99,6 +108,11 @@ protected boolean shouldNotFilter(HttpServletRequest request) {
return path.startsWith("/api/auth/register") ||
path.startsWith("/api/auth/login") ||
path.startsWith("/api/auth/verify-email") ||
path.startsWith("/api/documents/public");
path.startsWith("/api/documents/public") ||
path.startsWith("/api/auth/rt/refresh-token") ||
path.startsWith("/api/auth/rt/logout") ||
path.startsWith("/api/auth/forgot-password") ||
path.startsWith("/api/auth/reset-password") ||
path.startsWith("/api/auth/csrf");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public AuthenticationProvider authenticationProvider(
public PasswordEncoder passwordEncoder() {
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
// Configurations taken from OWASP recommendations

return new Argon2PasswordEncoder(
16, // Salt length
32, // Hash length
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.projektsse.backend.config;

import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
Expand All @@ -15,17 +15,28 @@
@EnableWebSecurity
public class SecurityConfig {

@Value("${app.cookie.secure}")
private boolean cookieSecure;

private final JwtFilter jwtFilter;

SecurityConfig(JwtFilter jwtFilter) {
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
private final CustomAccessDeniedHandler customAccessDeniedHandler;

SecurityConfig(JwtFilter jwtFilter,
CustomAuthenticationEntryPoint customAuthenticationEntryPoint,
CustomAccessDeniedHandler customAccessDeniedHandler
) {
this.jwtFilter = jwtFilter;
this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
this.customAccessDeniedHandler = customAccessDeniedHandler;
}

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) {
CookieCsrfTokenRepository repo = CookieCsrfTokenRepository.withHttpOnlyFalse();
repo.setCookieCustomizer(cookie -> {
cookie.secure(true);
cookie.secure(cookieSecure);
cookie.sameSite("Strict");
cookie.path("/");
});
Expand All @@ -38,6 +49,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) {
"/api/auth/register",
"/api/auth/verify-email",
"/api/documents/public",
"/api/documents/{docId}",
"/api/documents",
"/api/documents/public/search",
"/api/auth/forgot-password",
"/api/auth/reset-password"
Expand All @@ -49,8 +62,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) {
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.exceptionHandling(ex -> ex
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"))
.accessDeniedHandler((request, response, accessDeniedException) -> response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden"))
.authenticationEntryPoint(customAuthenticationEntryPoint)
.accessDeniedHandler(customAccessDeniedHandler)
)
.authorizeHttpRequests(auth -> auth
.requestMatchers(
Expand All @@ -62,7 +75,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) {
"/api/auth/rt/refresh-token",
"/api/auth/rt/logout",
"/api/auth/forgot-password",
"/api/auth/reset-password"
"/api/auth/reset-password",
"/api/auth/csrf"
).permitAll().anyRequest().authenticated()
)
.formLogin(AbstractHttpConfigurer::disable)
Expand Down
Loading