Usun symulowane logowanie przez Google i Facebook
Deploy Polska Lokalnie / deploy (push) Successful in 30s

Endpoint /api/auth/social byl atrapa bez prawdziwego OAuth: wydawal token
sesji dla dowolnego adresu e-mail podanego w zadaniu, bez hasla. Podanie
adresu administratora dawalo pelna sesje z rola ADMIN.

Na tym etapie logowanie ma byc wylacznie dla uzytkownikow systemu (e-mail
i haslo), wiec usuwam je w calosci:

- backend: endpoint, wpis permitAll w SecurityConfig, AuthService.socialLogin
  z pomocnikami i DTO SocialLoginRequest. Enum AuthProvider zostaje, bo
  istniejace konta moga miec GOOGLE/FACEBOOK.
- frontend: przyciski Google/Facebook w formularzach logowania i rejestracji,
  socialLogin w AuthContext oraz osierocone style.

Test regresyjny AuthControllerTest nie uruchomi sie jeszcze na H2 - start
kontekstu wywraca sie na split_part w SQL naprawiajacym nicki (ten sam blad
dotyczy istniejacego contextLoads). Zweryfikowane recznie na stacku
z PostgreSQL: /api/auth/social -> 403, logowanie e-mailem -> 200.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-21 14:20:32 +02:00
parent a3e4505194
commit 4453ef2305
8 changed files with 54 additions and 210 deletions
@@ -23,7 +23,6 @@ import pl.polskalokalnie.auth.dto.AuthResponse;
import pl.polskalokalnie.auth.dto.LoginRequest;
import pl.polskalokalnie.auth.dto.RegisterRequest;
import pl.polskalokalnie.auth.dto.RegisterResponse;
import pl.polskalokalnie.auth.dto.SocialLoginRequest;
import pl.polskalokalnie.auth.dto.UpdateProfileRequest;
import pl.polskalokalnie.auth.dto.UserResponse;
@@ -47,11 +46,6 @@ public class AuthController {
return authService.login(request);
}
@PostMapping("/social")
public AuthResponse social(@Valid @RequestBody SocialLoginRequest request) {
return authService.socialLogin(request);
}
// --- Aktywacja konta ---
@PostMapping("/activate")
@@ -19,7 +19,6 @@ import pl.polskalokalnie.auth.dto.AuthResponse;
import pl.polskalokalnie.auth.dto.LoginRequest;
import pl.polskalokalnie.auth.dto.RegisterRequest;
import pl.polskalokalnie.auth.dto.RegisterResponse;
import pl.polskalokalnie.auth.dto.SocialLoginRequest;
import pl.polskalokalnie.auth.dto.UpdateProfileRequest;
import pl.polskalokalnie.auth.dto.UserResponse;
import pl.polskalokalnie.user.AccountType;
@@ -261,45 +260,6 @@ public class AuthService {
return authToken;
}
/**
* Symulowane logowanie spoleczne: znajduje lub tworzy konto powiazane z dostawca.
* Nie ma tu prawdziwego OAuth - dane przychodza z frontendu jako demo.
*/
public AuthResponse socialLogin(SocialLoginRequest request) {
AuthProvider provider = request.provider();
String email = normalizeEmail(
request.email() != null && !request.email().isBlank()
? request.email()
: defaultSocialEmail(provider));
String name = request.fullName() != null && !request.fullName().isBlank()
? request.fullName().trim()
: defaultSocialName(provider);
textModerationService.validateOrThrow(name);
AppUser user = userRepository.findByEmailIgnoreCase(email).orElseGet(() -> {
if (blockedEmailRepository.existsByEmailIgnoreCase(email)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Ten adres e-mail został zablokowany i nie można go już użyć do rejestracji");
}
AppUser created = new AppUser();
created.setEmail(email);
created.setFullName(name);
created.setNick(nickService.generateFromName(name, email, null));
created.setRole(Role.USER);
created.setProvider(provider);
// Logowanie spoleczne nie wymaga rekopisania danych, wiec konto jest od razu zweryfikowane.
created.setVerified(true);
return userRepository.save(created);
});
if (user.isBlocked()) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Konto zostało zablokowane");
}
leadSyncService.syncUser(user);
return buildAuthResponse(user);
}
// Odstep, po ktorym wejscie do aplikacji (odswiezenie /auth/me) na nowo odnotowuje aktywnosc.
// Bez tego zapisywalibysmy do bazy przy kazdym zadaniu profilu - a wystarczy zgrubna dokladnosc.
private static final Duration LAST_ACTIVE_REFRESH = Duration.ofMinutes(2);
@@ -491,20 +451,4 @@ public class AuthService {
String source = fullName != null && !fullName.isBlank() ? fullName : user.getFullName();
return nickService.generateFromName(source, user.getEmail(), user);
}
private String defaultSocialEmail(AuthProvider provider) {
return switch (provider) {
case GOOGLE -> "demo.google@gmail.com";
case FACEBOOK -> "demo.facebook@facebook.com";
case LOCAL -> "demo.local@polskalokalnie.pl";
};
}
private String defaultSocialName(AuthProvider provider) {
return switch (provider) {
case GOOGLE -> "Użytkownik Google";
case FACEBOOK -> "Użytkownik Facebook";
case LOCAL -> "Użytkownik";
};
}
}
@@ -35,7 +35,7 @@ public class SecurityConfig {
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers("/error").permitAll()
.requestMatchers("/api/auth/register", "/api/auth/login", "/api/auth/social").permitAll()
.requestMatchers("/api/auth/register", "/api/auth/login").permitAll()
.requestMatchers("/api/auth/activate", "/api/auth/resend-activation",
"/api/auth/forgot-password", "/api/auth/reset-password",
"/api/auth/verify-phone", "/api/auth/resend-phone-otp").permitAll()
@@ -1,15 +0,0 @@
package pl.polskalokalnie.auth.dto;
import jakarta.validation.constraints.NotNull;
import pl.polskalokalnie.user.AuthProvider;
/**
* Symulowane logowanie spoleczne. W wersji produkcyjnej email/name pochodzilyby
* z tokenu dostawcy OAuth (Google/Facebook); tutaj przychodza z frontendu jako dane demo.
*/
public record SocialLoginRequest(
@NotNull AuthProvider provider,
String email,
String fullName
) {
}
@@ -0,0 +1,44 @@
package pl.polskalokalnie.auth;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
@SpringBootTest
@AutoConfigureMockMvc
class AuthControllerTest {
@Autowired
private MockMvc mockMvc;
// Regresja: symulowane logowanie spoleczne wydawalo token dla dowolnego adresu e-mail
// bez hasla - wystarczylo podac adres administratora, zeby dostac jego sesje.
@Test
void socialLoginEndpointDoesNotIssueToken() throws Exception {
mockMvc.perform(post("/api/auth/social")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"provider\":\"GOOGLE\",\"email\":\"admin@softspm.com\"}"))
.andExpect(status().is4xxClientError())
.andExpect(content().string(not(containsString("token"))));
}
@Test
void adminCanLogInWithEmailAndPassword() throws Exception {
mockMvc.perform(post("/api/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"email\":\"admin@softspm.com\",\"password\":\"Admin123!\"}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").isNotEmpty())
.andExpect(jsonPath("$.user.role").value("ADMIN"));
}
}