diff --git a/.idea/runConfigurations/Flashcards_DEVMODE.xml b/.idea/runConfigurations/Flashcards_DEVMODE.xml
new file mode 100644
index 0000000..cdf692a
--- /dev/null
+++ b/.idea/runConfigurations/Flashcards_DEVMODE.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Vue_Server_DEVMODE.xml b/.idea/runConfigurations/Vue_Server_DEVMODE.xml
new file mode 100644
index 0000000..8780a4e
--- /dev/null
+++ b/.idea/runConfigurations/Vue_Server_DEVMODE.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/feature/cards/components/CardItemScroller.vue b/frontend/src/feature/cards/components/CardItemScroller.vue
index 5330a59..50d9758 100644
--- a/frontend/src/feature/cards/components/CardItemScroller.vue
+++ b/frontend/src/feature/cards/components/CardItemScroller.vue
@@ -61,41 +61,4 @@ const openCard = (id: string) => {
console.log("Opening card with id: " + id);
router.push(`/card/${props.categoryId}/${id}`);
}
-// ------------------- TEST DATA -------------------
-// ---uncomment if on npm run mode - and comment out the fetchCardsPage() call
-// const load = async ({done} : {done: Function}) => {
-// console.log(`Loading cards ... Now we have ${items.value.length} items.`);
-// items.value.push({
-// id: "123",
-// title: "A math question",
-// type: CardType.SIMPLEQA,
-// });
-// items.value.push({
-// id: "456",
-// title: "A Java question",
-// type: CardType.SINGLE_CHOICE,
-// });
-// items.value.push({
-// id: "456",
-// title: "Another Java question",
-// type: CardType.SINGLE_CHOICE,
-// });
-// items.value.push({
-// id: "456",
-// title: "Another Math question",
-// type: CardType.SINGLE_CHOICE,
-// });
-// items.value.push({
-// id: "456",
-// title: "Another Python question",
-// type: CardType.SINGLE_CHOICE,
-// });
-// items.value.push({
-// id: "456",
-// title: "A Python question",
-// type: CardType.SINGLE_CHOICE,
-// });
-// done('ok');
-// }
-// load({done: console.log});
diff --git a/server/src/main/java/org/hyperskill/community/flashcards/common/AuthenticationResolver.java b/server/src/main/java/org/hyperskill/community/flashcards/common/AuthenticationResolver.java
index 0e6d891..4644b39 100644
--- a/server/src/main/java/org/hyperskill/community/flashcards/common/AuthenticationResolver.java
+++ b/server/src/main/java/org/hyperskill/community/flashcards/common/AuthenticationResolver.java
@@ -1,14 +1,22 @@
package org.hyperskill.community.flashcards.common;
+import lombok.RequiredArgsConstructor;
+import org.springframework.core.env.Environment;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Component;
@Component
+@RequiredArgsConstructor
public class AuthenticationResolver {
+ private final Environment env;
+
public String resolveUsername() {
+ if (Boolean.TRUE.equals(env.getProperty("DEV_MODE", Boolean.class, false))) {
+ return env.getProperty("DEV_USER", "test1@test.com");
+ }
var authentication = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return switch (authentication) {
case OidcUser oidcUser -> oidcUser.getSubject();
diff --git a/server/src/main/java/org/hyperskill/community/flashcards/config/WebSecurityConfiguration.java b/server/src/main/java/org/hyperskill/community/flashcards/config/WebSecurityConfiguration.java
index d53d4f0..8438556 100644
--- a/server/src/main/java/org/hyperskill/community/flashcards/config/WebSecurityConfiguration.java
+++ b/server/src/main/java/org/hyperskill/community/flashcards/config/WebSecurityConfiguration.java
@@ -1,6 +1,8 @@
package org.hyperskill.community.flashcards.config;
+import lombok.extern.slf4j.Slf4j;
import org.hyperskill.community.flashcards.registration.User;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -13,42 +15,76 @@
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import java.util.List;
import java.util.Optional;
import static org.springframework.security.config.Customizer.withDefaults;
/**
- * new Spring security 6.0 style provision of SecurityFilterChain bean with the security configuration,
+ * Spring security 6.x style provision of SecurityFilterChain bean with the security configuration,
* as well as PasswordProvider and AuthenticationManager that makes use of our UserDetails persistence.
*/
@Configuration
@EnableWebSecurity
+@Slf4j
public class WebSecurityConfiguration {
@Bean
+ @ConditionalOnProperty(name = "DEV_MODE", havingValue = "false", matchIfMissing = true)
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(CsrfConfigurer::disable)
.oauth2ResourceServer(auth -> auth.jwt(withDefaults()))
+ .oauth2Login(withDefaults())
+ .oauth2Client(withDefaults())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/register.html", "/js/register.js", "/css/register.css").permitAll()
.requestMatchers(HttpMethod.POST, "/api/register").permitAll()
- .anyRequest().authenticated())
- .oauth2Login(withDefaults())
- .oauth2Client(withDefaults())
- .build();
+ .anyRequest().authenticated()
+ ).build();
}
@Bean
public UserDetailsService userDetailsService(MongoTemplate mongoTemplate) {
return username ->
- Optional.ofNullable(mongoTemplate.findById(username, User.class))
- .orElseThrow(() -> new UsernameNotFoundException("User not found."));
+ Optional.ofNullable(mongoTemplate.findById(username, User.class))
+ .orElseThrow(() -> new UsernameNotFoundException("User not found."));
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
+
+ @Bean
+ @ConditionalOnProperty(name = "DEV_MODE", havingValue = "true")
+ public SecurityFilterChain filterChainDevMode(HttpSecurity http) throws Exception {
+ log.warn("Running in DEV_MODE, permitting all requests.");
+ return http
+ .csrf(CsrfConfigurer::disable)
+ .cors(withDefaults())
+ .authorizeHttpRequests(
+ auth -> auth.anyRequest().permitAll()
+ ).build();
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = "DEV_MODE", havingValue = "true")
+ public CorsConfigurationSource corsConfigurationSource() {
+ log.info("Configuring CORS for DEV MODE");
+ var configuration = new CorsConfiguration();
+ configuration.setAllowedOrigins(List.of("http://localhost:3000"));
+ configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
+ configuration.setAllowedHeaders(List.of("Authorization",
+ "Access-Control-Allow-Origin", "Content-Type"));
+ configuration.setExposedHeaders(List.of("Authorization",
+ "Access-Control-Allow-Origin", "Content-Type"));
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/api/**", configuration);
+ return source;
+ }
}
diff --git a/server/src/test/java/org/hyperskill/community/flashcards/common/AuthenticationResolverTest.java b/server/src/test/java/org/hyperskill/community/flashcards/common/AuthenticationResolverTest.java
index c4a6cab..24dc2fe 100644
--- a/server/src/test/java/org/hyperskill/community/flashcards/common/AuthenticationResolverTest.java
+++ b/server/src/test/java/org/hyperskill/community/flashcards/common/AuthenticationResolverTest.java
@@ -5,8 +5,10 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoSettings;
+import org.springframework.core.env.Environment;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -29,9 +31,12 @@ class AuthenticationResolverTest {
AuthenticationResolver resolver;
+ @Mock
+ Environment env;
+
@BeforeEach
void setup() {
- resolver = new AuthenticationResolver();
+ resolver = new AuthenticationResolver(env);
}
static Stream whenJwtOrOidcAuthentication_resolverWorks() {