From 3b02c4ab1dbed7fce597bdbe5ca0b00d1afb71ea Mon Sep 17 00:00:00 2001 From: Hamza Remmal <hamzaremmalpriv@gmail.com> Date: Thu, 10 Aug 2023 14:22:00 +0100 Subject: [PATCH] Add possibility to have multiple Authentication based on the request + add publisher to publish the authentication events --- .idea/encodings.xml | 1 + .idea/misc.xml | 2 + .../AutogradeAuthenticationConverter.java | 26 ++++++++++++ .../auth/events/AuthenticationEvents.java | 31 ++++++++++++++ .../filter/ApiKeyAuthenticationFilter.java | 41 ++++++++++++++----- .../ApiKeyAuthenticationProvider.java | 2 +- .../config/AuthenticationConfiguration.java | 23 +++++++++++ 7 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 autograde-service/src/main/java/ch/epfl/autograde/auth/converter/AutogradeAuthenticationConverter.java create mode 100644 autograde-service/src/main/java/ch/epfl/autograde/auth/events/AuthenticationEvents.java create mode 100644 autograde-service/src/main/java/ch/epfl/autograde/config/AuthenticationConfiguration.java diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 0903db4a..cc6b8358 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="Encoding"> + <file url="file://$PROJECT_DIR$/autograde-service/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/moodle-grading-service/src/main/java" charset="UTF-8" /> <file url="file://$PROJECT_DIR$/moodle-grading-service/src/main/resources" charset="UTF-8" /> </component> diff --git a/.idea/misc.xml b/.idea/misc.xml index 99e50705..6a1cc841 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,9 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="MavenProjectsManager"> <option name="originalFiles"> <list> <option value="$PROJECT_DIR$/moodle-grading-service/pom.xml" /> + <option value="$PROJECT_DIR$/autograde-service/pom.xml" /> </list> </option> </component> diff --git a/autograde-service/src/main/java/ch/epfl/autograde/auth/converter/AutogradeAuthenticationConverter.java b/autograde-service/src/main/java/ch/epfl/autograde/auth/converter/AutogradeAuthenticationConverter.java new file mode 100644 index 00000000..ecf68f2e --- /dev/null +++ b/autograde-service/src/main/java/ch/epfl/autograde/auth/converter/AutogradeAuthenticationConverter.java @@ -0,0 +1,26 @@ +package ch.epfl.autograde.auth.converter; + +import ch.epfl.autograde.auth.ApiKeyAuthentication; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationConverter; +import org.springframework.stereotype.Component; + +/** + * ??? + * + * @author Hamza REMMAL (hamza.remmal@epfl.ch) + */ +@Component +public final class AutogradeAuthenticationConverter implements AuthenticationConverter { + + private static final String API_KEY_HEADER_ENTRY = "API-KEY"; + + @Override + public Authentication convert(HttpServletRequest request) { + // HR : Fetch the API-KEY from the header + var key = request.getHeader(API_KEY_HEADER_ENTRY); + return new ApiKeyAuthentication(key, false); + } + +} diff --git a/autograde-service/src/main/java/ch/epfl/autograde/auth/events/AuthenticationEvents.java b/autograde-service/src/main/java/ch/epfl/autograde/auth/events/AuthenticationEvents.java new file mode 100644 index 00000000..2850bb9f --- /dev/null +++ b/autograde-service/src/main/java/ch/epfl/autograde/auth/events/AuthenticationEvents.java @@ -0,0 +1,31 @@ +package ch.epfl.autograde.auth.events; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; +import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent; +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.stereotype.Component; + +/** + * ??? + * @author Hamza REMMAL (hamza.remmal@epfl.ch) + * @since 1.0 + * @see <a href="https://docs.spring.io/spring-security/reference/servlet/authentication/events.html">docs.spring.io</a> + */ +@Slf4j +@Component +public final class AuthenticationEvents { + + @EventListener + public void onSuccess(AuthenticationSuccessEvent success) { + // TODO HR : DO NOT IMPLEMENT IT, CAN BE QUITE NOISY IN THE LOGS + } + + @EventListener + public void onFailure(AuthenticationFailureBadCredentialsEvent failures) { + log.error("An authentication request has failed from source {}", failures.getSource(), failures.getException()); + // ... + } + +} diff --git a/autograde-service/src/main/java/ch/epfl/autograde/auth/filter/ApiKeyAuthenticationFilter.java b/autograde-service/src/main/java/ch/epfl/autograde/auth/filter/ApiKeyAuthenticationFilter.java index f4241e13..36f302c4 100644 --- a/autograde-service/src/main/java/ch/epfl/autograde/auth/filter/ApiKeyAuthenticationFilter.java +++ b/autograde-service/src/main/java/ch/epfl/autograde/auth/filter/ApiKeyAuthenticationFilter.java @@ -1,12 +1,16 @@ package ch.epfl.autograde.auth.filter; -import ch.epfl.autograde.auth.ApiKeyAuthentication; +import ch.epfl.autograde.auth.converter.AutogradeAuthenticationConverter; import ch.epfl.autograde.auth.manager.ApiKeyAuthenticationManager; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationEventPublisher; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; @@ -18,21 +22,28 @@ import java.io.IOException; * * @author Hamza REMMAL (hamza.remmal@epfl.ch) */ +@Slf4j @Component -public class ApiKeyAuthenticationFilter extends OncePerRequestFilter { +public final class ApiKeyAuthenticationFilter extends OncePerRequestFilter { - private static final String API_KEY_HEADER_ENTRY = "API-KEY"; + private final AutogradeAuthenticationConverter converter; /** ??? */ private final ApiKeyAuthenticationManager manager; + private final AuthenticationEventPublisher publisher; + /** * ??? * @param manager ??? */ @Autowired - public ApiKeyAuthenticationFilter(ApiKeyAuthenticationManager manager) { + public ApiKeyAuthenticationFilter(ApiKeyAuthenticationManager manager, + AutogradeAuthenticationConverter converter, + AuthenticationEventPublisher publisher) { this.manager = manager; + this.converter = converter; + this.publisher = publisher; } /** @@ -44,13 +55,21 @@ public class ApiKeyAuthenticationFilter extends OncePerRequestFilter { * @throws IOException ??? */ @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - // HR : Fetch the API-KEY from the header - var key = request.getHeader(API_KEY_HEADER_ENTRY); - // HR : Request the authentication from the authentication manager - var auth = manager.authenticate(new ApiKeyAuthentication(key, false)); - // HR : Set the authentication in the SecurityContext - SecurityContextHolder.getContext().setAuthentication(auth); + protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // HR : Fetch the authentication object based on the request + var authentication = converter.convert(request); + try { + // HR : Authenticate using the manager + var auth = manager.authenticate(authentication); + // HR : Set the authentication in the SecurityContext + SecurityContextHolder.getContext().setAuthentication(auth); + // HR : Inform the publisher + publisher.publishAuthenticationSuccess(authentication); + }catch (AuthenticationException e){ + log.trace("Authentication using the filter failed", e); + // hr : Inform the publisher + publisher.publishAuthenticationFailure(e, authentication); + } // HR : Complete the chain of filters filterChain.doFilter(request, response); } diff --git a/autograde-service/src/main/java/ch/epfl/autograde/auth/provider/ApiKeyAuthenticationProvider.java b/autograde-service/src/main/java/ch/epfl/autograde/auth/provider/ApiKeyAuthenticationProvider.java index 09125953..414e28db 100644 --- a/autograde-service/src/main/java/ch/epfl/autograde/auth/provider/ApiKeyAuthenticationProvider.java +++ b/autograde-service/src/main/java/ch/epfl/autograde/auth/provider/ApiKeyAuthenticationProvider.java @@ -16,7 +16,7 @@ import static java.util.Objects.isNull; * @author Hamza REMMAL (hamza.remmal@epfl.ch) */ @Component -public class ApiKeyAuthenticationProvider implements AuthenticationProvider { +public final class ApiKeyAuthenticationProvider implements AuthenticationProvider { /** ??? */ private final String apiKey; diff --git a/autograde-service/src/main/java/ch/epfl/autograde/config/AuthenticationConfiguration.java b/autograde-service/src/main/java/ch/epfl/autograde/config/AuthenticationConfiguration.java new file mode 100644 index 00000000..1dbc9736 --- /dev/null +++ b/autograde-service/src/main/java/ch/epfl/autograde/config/AuthenticationConfiguration.java @@ -0,0 +1,23 @@ +package ch.epfl.autograde.config; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationEventPublisher; +import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; + +@Configuration +public class AuthenticationConfiguration { + + /** + * ??? + * @param publisher ??? + * @return ??? + * @see <a href="https://docs.spring.io/spring-security/reference/servlet/authentication/events.html">docs.spring.io</a> + */ + @Bean + public AuthenticationEventPublisher auth_publisher(ApplicationEventPublisher publisher) { + return new DefaultAuthenticationEventPublisher(publisher); + } + +} -- GitLab