feat(join): A4 — Join household (accept invite) #61
@@ -24,7 +24,7 @@ public class SecurityConfig {
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/v1/auth/signup", "/v1/auth/login").permitAll()
|
||||
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
|
||||
.requestMatchers("/v1/invites/*").permitAll()
|
||||
.requestMatchers("/v1/invites/**").permitAll()
|
||||
.requestMatchers("/v1/admin/**").hasAuthority("ROLE_ADMIN")
|
||||
.anyRequest().authenticated())
|
||||
.exceptionHandling(ex -> ex
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
class SecurityConfigTest extends AbstractIntegrationTest {
|
||||
@@ -33,6 +34,15 @@ class SecurityConfigTest extends AbstractIntegrationTest {
|
||||
.andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
@Test
|
||||
void inviteAcceptEndpointIsAccessibleWithoutAuthentication() throws Exception {
|
||||
// 400 = validation error (empty body), but NOT 401 — proves the path is permitted
|
||||
mockMvc.perform(post("/v1/invites/ANYCODE/accept")
|
||||
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
|
||||
.content("{}"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void protectedEndpointRequiresAuthentication() throws Exception {
|
||||
mockMvc.perform(get("/v1/households/mine"))
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<!-- Desktop layout (≥ 1024px): two-column side by side -->
|
||||
<div class="flex min-h-screen flex-col lg:flex-row">
|
||||
<!-- Left / top: green-tint panel -->
|
||||
<div class="bg-[var(--green-tint)] p-6 lg:flex lg:w-[400px] lg:flex-shrink-0 lg:items-center lg:justify-center lg:p-12">
|
||||
<div class="bg-[var(--green-dark)] p-6 lg:flex lg:w-[400px] lg:flex-shrink-0 lg:items-center lg:justify-center lg:p-12">
|
||||
<HouseholdIdentityPanel
|
||||
householdName={data.householdName ?? ''}
|
||||
inviterName={data.inviterName ?? ''}
|
||||
|
||||
@@ -2,38 +2,38 @@
|
||||
let { householdName, inviterName }: { householdName: string; inviterName: string } = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center gap-4 rounded-2xl bg-[var(--green-tint)] p-6 text-center">
|
||||
<div class="flex flex-col items-center gap-4 rounded-2xl bg-[var(--green-dark)] p-6 text-center">
|
||||
<!-- App logo -->
|
||||
<span class="text-[48px]" aria-hidden="true">🥗</span>
|
||||
|
||||
<!-- Household name -->
|
||||
<div>
|
||||
<h2
|
||||
class="font-[var(--font-display)] text-[22px] font-semibold tracking-[-0.02em] text-[var(--color-text)]"
|
||||
class="font-[var(--font-display)] text-[22px] font-semibold tracking-[-0.02em] text-white"
|
||||
>
|
||||
{householdName}
|
||||
</h2>
|
||||
<p class="mt-1 font-[var(--font-sans)] text-[12px] text-[var(--color-text-muted)]">
|
||||
<p class="mt-1 font-[var(--font-sans)] text-[12px] text-[var(--green-light)]">
|
||||
Eingeladen von {inviterName}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Permissions info box -->
|
||||
<div class="w-full rounded-xl bg-white/20 px-4 py-3 text-left">
|
||||
<p class="mb-2 font-[var(--font-sans)] text-[11px] font-medium uppercase tracking-wide text-[var(--color-text-muted)]">
|
||||
<div class="w-full rounded-xl bg-white/10 px-4 py-3 text-left">
|
||||
<p class="mb-2 font-[var(--font-sans)] text-[11px] font-medium uppercase tracking-wide text-[var(--green-light)]">
|
||||
Als Mitglied kannst du
|
||||
</p>
|
||||
<ul class="flex flex-col gap-1.5">
|
||||
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-[var(--color-text)]">
|
||||
<span class="text-[var(--green)] font-semibold" aria-hidden="true">✓</span>
|
||||
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-white">
|
||||
<span class="font-semibold text-[var(--green-light)]" aria-hidden="true">✓</span>
|
||||
Wochenplan einsehen
|
||||
</li>
|
||||
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-[var(--color-text)]">
|
||||
<span class="text-[var(--green)] font-semibold" aria-hidden="true">✓</span>
|
||||
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-white">
|
||||
<span class="font-semibold text-[var(--green-light)]" aria-hidden="true">✓</span>
|
||||
Einkaufsliste abhaken
|
||||
</li>
|
||||
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-[var(--color-text)]">
|
||||
<span class="text-[var(--green)] font-semibold" aria-hidden="true">✓</span>
|
||||
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-white">
|
||||
<span class="font-semibold text-[var(--green-light)]" aria-hidden="true">✓</span>
|
||||
Artikel zur Liste hinzufügen
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Reference in New Issue
Block a user