Add Checkstyle and Prettier to BFF #65
@ -3,5 +3,4 @@ package se.su.dsv.studentportalen.bff.config;
|
|||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
@ConfigurationProperties("se.su.dsv.backend-api")
|
@ConfigurationProperties("se.su.dsv.backend-api")
|
||||||
public record BackendApiConfiguration(String daisyUrl) {
|
public record BackendApiConfiguration(String daisyUrl) {}
|
||||||
}
|
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
public record FrontendConfiguration(String url) {
|
public record FrontendConfiguration(String url) {
|
||||||
public FrontendConfiguration {
|
public FrontendConfiguration {
|
||||||
if (url == null || url.isBlank()) {
|
if (url == null || url.isBlank()) {
|
||||||
throw new IllegalArgumentException("se.su.dsv.frontend.url must not be null or blank");
|
throw new IllegalArgumentException(
|
||||||
|
"se.su.dsv.frontend.url must not be null or blank"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package se.su.dsv.studentportalen.bff.config;
|
package se.su.dsv.studentportalen.bff.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
@ -7,34 +8,42 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint;
|
import se.su.dsv.studentportalen.bff.login.BFFAuthenticationEntryPoint;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SecurityConfiguration {
|
public class SecurityConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(
|
public SecurityFilterChain securityFilterChain(
|
||||||
HttpSecurity http,
|
HttpSecurity http,
|
||||||
FrontendConfiguration frontendConfiguration)
|
FrontendConfiguration frontendConfiguration
|
||||||
throws Exception
|
) throws Exception {
|
||||||
{
|
http.exceptionHandling(exception ->
|
||||||
http.exceptionHandling(exception -> exception
|
exception.authenticationEntryPoint(new BFFAuthenticationEntryPoint())
|
||||||
.authenticationEntryPoint(new BFFAuthenticationEntryPoint()));
|
);
|
||||||
http.oauth2Login(login -> login
|
http.oauth2Login(login ->
|
||||||
.defaultSuccessUrl(frontendConfiguration.url(), true));
|
login.defaultSuccessUrl(frontendConfiguration.url(), true)
|
||||||
http.authorizeHttpRequests(authorize -> authorize
|
);
|
||||||
.requestMatchers("/swagger", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
|
http.authorizeHttpRequests(authorize ->
|
||||||
.anyRequest().authenticated());
|
authorize
|
||||||
http.cors(cors -> cors
|
.requestMatchers("/swagger", "/swagger-ui/**", "/v3/api-docs/**")
|
||||||
.configurationSource(_ -> frontendOnlyCors(frontendConfiguration)));
|
.permitAll()
|
||||||
|
.anyRequest()
|
||||||
|
.authenticated()
|
||||||
|
);
|
||||||
|
http.cors(cors ->
|
||||||
|
cors.configurationSource(_ -> frontendOnlyCors(frontendConfiguration))
|
||||||
|
);
|
||||||
http.csrf(csrf -> csrf.spa());
|
http.csrf(csrf -> csrf.spa());
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CorsConfiguration frontendOnlyCors(FrontendConfiguration frontendConfiguration) {
|
private static CorsConfiguration frontendOnlyCors(
|
||||||
|
FrontendConfiguration frontendConfiguration
|
||||||
|
) {
|
||||||
var corsConfiguration = new CorsConfiguration();
|
var corsConfiguration = new CorsConfiguration();
|
||||||
corsConfiguration.setAllowedOrigins(List.of(frontendConfiguration.url()));
|
corsConfiguration.setAllowedOrigins(List.of(frontendConfiguration.url()));
|
||||||
corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
|
corsConfiguration.setAllowedMethods(
|
||||||
|
List.of("GET", "POST", "PUT", "DELETE")
|
||||||
|
);
|
||||||
|
|
||||||
// Allow the frontend to see the X-Authorization-Url header
|
// Allow the frontend to see the X-Authorization-Url header
|
||||||
corsConfiguration.setExposedHeaders(List.of("X-Authorization-Url"));
|
corsConfiguration.setExposedHeaders(List.of("X-Authorization-Url"));
|
||||||
@ -45,7 +54,9 @@ public class SecurityConfiguration {
|
|||||||
// Content-Type is allowed by default but with a restriction on the value
|
// Content-Type is allowed by default but with a restriction on the value
|
||||||
// The restriction does not allow "application/json" so we add it as an allowed header
|
// The restriction does not allow "application/json" so we add it as an allowed header
|
||||||
// X-XSRF-TOKEN is needed for CSRF protection
|
// X-XSRF-TOKEN is needed for CSRF protection
|
||||||
corsConfiguration.setAllowedHeaders(List.of("Content-Type", "X-XSRF-TOKEN"));
|
corsConfiguration.setAllowedHeaders(
|
||||||
|
List.of("Content-Type", "X-XSRF-TOKEN")
|
||||||
|
);
|
||||||
return corsConfiguration;
|
return corsConfiguration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,8 +24,8 @@ public class ProfileController {
|
|||||||
|
|
||||||
@GetMapping("/profile")
|
@GetMapping("/profile")
|
||||||
public ProfileResponse getProfile(
|
public ProfileResponse getProfile(
|
||||||
@AuthenticationPrincipal(errorOnInvalidType = true) OAuth2User currentUser)
|
@AuthenticationPrincipal(errorOnInvalidType = true) OAuth2User currentUser
|
||||||
{
|
) {
|
||||||
return service.getProfile(currentUser);
|
return service.getProfile(currentUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
package se.su.dsv.studentportalen.bff.controller;
|
package se.su.dsv.studentportalen.bff.controller;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Profile;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.client.RestClient;
|
|
||||||
import se.su.dsv.studentportalen.bff.config.BackendApiConfiguration;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.StructuredTaskScope;
|
import java.util.concurrent.StructuredTaskScope;
|
||||||
import java.util.concurrent.StructuredTaskScope.Subtask;
|
import java.util.concurrent.StructuredTaskScope.Subtask;
|
||||||
|
import org.springframework.context.annotation.Profile;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
|
import se.su.dsv.studentportalen.bff.config.BackendApiConfiguration;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/test")
|
@RequestMapping("/test")
|
||||||
@ -34,7 +33,8 @@ public class TestController {
|
|||||||
// Pick the scope based on the desired behaviour
|
// Pick the scope based on the desired behaviour
|
||||||
try (var scope = StructuredTaskScope.open()) {
|
try (var scope = StructuredTaskScope.open()) {
|
||||||
Subtask<String> nameTask = scope.fork(() -> {
|
Subtask<String> nameTask = scope.fork(() -> {
|
||||||
String name = restClient.get()
|
String name = restClient
|
||||||
|
.get()
|
||||||
.uri("/test/name")
|
.uri("/test/name")
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.body(String.class);
|
.body(String.class);
|
||||||
@ -42,7 +42,8 @@ public class TestController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Subtask<String> emailTask = scope.fork(() -> {
|
Subtask<String> emailTask = scope.fork(() -> {
|
||||||
String email = restClient.get()
|
String email = restClient
|
||||||
|
.get()
|
||||||
.uri("/test/email")
|
.uri("/test/email")
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.body(String.class);
|
.body(String.class);
|
||||||
@ -50,10 +51,11 @@ public class TestController {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Subtask<DaisyProfile> daisyProfile = scope.fork(() -> {
|
Subtask<DaisyProfile> daisyProfile = scope.fork(() -> {
|
||||||
DaisyProfile profile = restClient.get()
|
DaisyProfile profile = restClient
|
||||||
.uri(backendApiConfiguration.daisyUrl(), builder -> builder
|
.get()
|
||||||
.path("/profile")
|
.uri(backendApiConfiguration.daisyUrl(), builder ->
|
||||||
.build())
|
builder.path("/profile").build()
|
||||||
|
)
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.body(DaisyProfile.class);
|
.body(DaisyProfile.class);
|
||||||
return profile;
|
return profile;
|
||||||
@ -65,7 +67,8 @@ public class TestController {
|
|||||||
return "Hello, I am %s and my email is %s. My Daisy profile is: %s".formatted(
|
return "Hello, I am %s and my email is %s. My Daisy profile is: %s".formatted(
|
||||||
nameTask.get(),
|
nameTask.get(),
|
||||||
emailTask.get(),
|
emailTask.get(),
|
||||||
daisyProfile.get());
|
daisyProfile.get()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
package se.su.dsv.studentportalen.bff.dto.response;
|
package se.su.dsv.studentportalen.bff.dto.response;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User profile information.
|
* User profile information.
|
||||||
*/
|
*/
|
||||||
public record ProfileResponse(
|
public record ProfileResponse(
|
||||||
@JsonProperty(value = "name", required = true)
|
@JsonProperty(value = "name", required = true) String name,
|
||||||
String name,
|
|
||||||
|
|
||||||
@JsonProperty(value = "language", required = true)
|
@JsonProperty(value = "language", required = true) Language language
|
||||||
Language language)
|
) {
|
||||||
{
|
|
||||||
public enum Language {
|
public enum Language {
|
||||||
@JsonProperty("sv") SWEDISH,
|
@JsonProperty("sv")
|
||||||
@JsonProperty("en") ENGLISH
|
SWEDISH,
|
||||||
|
@JsonProperty("en")
|
||||||
|
ENGLISH,
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProfileResponse {
|
public ProfileResponse {
|
||||||
|
|||||||
@ -7,12 +7,13 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
|||||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||||
|
|
||||||
public class BFFAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
public class BFFAuthenticationEntryPoint implements AuthenticationEntryPoint {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commence(
|
public void commence(
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
AuthenticationException authException)
|
AuthenticationException authException
|
||||||
{
|
) {
|
||||||
String loginUri = ServletUriComponentsBuilder.fromRequest(request)
|
String loginUri = ServletUriComponentsBuilder.fromRequest(request)
|
||||||
.replacePath("/oauth2/authorization/studentportalen")
|
.replacePath("/oauth2/authorization/studentportalen")
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user