fix(join): permit /v1/invites/** (not just /*) + match panel color to login

- SecurityConfig: /** covers /v1/invites/{code}/accept (two path segments);
  /* only matched one segment so the accept endpoint was returning 401
- HouseholdIdentityPanel + page: use --green-dark bg (matching BrandPanel
  on login) instead of --green-tint; text updated to white/--green-light

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 22:00:47 +02:00
parent c5ec3396b2
commit 6aed303627
4 changed files with 23 additions and 13 deletions

View File

@@ -24,7 +24,7 @@ public class SecurityConfig {
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers("/v1/auth/signup", "/v1/auth/login").permitAll() .requestMatchers("/v1/auth/signup", "/v1/auth/login").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/v1/invites/*").permitAll() .requestMatchers("/v1/invites/**").permitAll()
.requestMatchers("/v1/admin/**").hasAuthority("ROLE_ADMIN") .requestMatchers("/v1/admin/**").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated()) .anyRequest().authenticated())
.exceptionHandling(ex -> ex .exceptionHandling(ex -> ex

View File

@@ -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.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.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
class SecurityConfigTest extends AbstractIntegrationTest { class SecurityConfigTest extends AbstractIntegrationTest {
@@ -33,6 +34,15 @@ class SecurityConfigTest extends AbstractIntegrationTest {
.andExpect(status().isNotFound()); .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 @Test
void protectedEndpointRequiresAuthentication() throws Exception { void protectedEndpointRequiresAuthentication() throws Exception {
mockMvc.perform(get("/v1/households/mine")) mockMvc.perform(get("/v1/households/mine"))

View File

@@ -26,7 +26,7 @@
<!-- Desktop layout (≥ 1024px): two-column side by side --> <!-- Desktop layout (≥ 1024px): two-column side by side -->
<div class="flex min-h-screen flex-col lg:flex-row"> <div class="flex min-h-screen flex-col lg:flex-row">
<!-- Left / top: green-tint panel --> <!-- 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 <HouseholdIdentityPanel
householdName={data.householdName ?? ''} householdName={data.householdName ?? ''}
inviterName={data.inviterName ?? ''} inviterName={data.inviterName ?? ''}

View File

@@ -2,38 +2,38 @@
let { householdName, inviterName }: { householdName: string; inviterName: string } = $props(); let { householdName, inviterName }: { householdName: string; inviterName: string } = $props();
</script> </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 --> <!-- App logo -->
<span class="text-[48px]" aria-hidden="true">🥗</span> <span class="text-[48px]" aria-hidden="true">🥗</span>
<!-- Household name --> <!-- Household name -->
<div> <div>
<h2 <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} {householdName}
</h2> </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} Eingeladen von {inviterName}
</p> </p>
</div> </div>
<!-- Permissions info box --> <!-- Permissions info box -->
<div class="w-full rounded-xl bg-white/20 px-4 py-3 text-left"> <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(--color-text-muted)]"> <p class="mb-2 font-[var(--font-sans)] text-[11px] font-medium uppercase tracking-wide text-[var(--green-light)]">
Als Mitglied kannst du Als Mitglied kannst du
</p> </p>
<ul class="flex flex-col gap-1.5"> <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)]"> <li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-white">
<span class="text-[var(--green)] font-semibold" aria-hidden="true"></span> <span class="font-semibold text-[var(--green-light)]" aria-hidden="true"></span>
Wochenplan einsehen Wochenplan einsehen
</li> </li>
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-[var(--color-text)]"> <li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-white">
<span class="text-[var(--green)] font-semibold" aria-hidden="true"></span> <span class="font-semibold text-[var(--green-light)]" aria-hidden="true"></span>
Einkaufsliste abhaken Einkaufsliste abhaken
</li> </li>
<li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-[var(--color-text)]"> <li class="flex items-center gap-2 font-[var(--font-sans)] text-[13px] text-white">
<span class="text-[var(--green)] font-semibold" aria-hidden="true"></span> <span class="font-semibold text-[var(--green-light)]" aria-hidden="true"></span>
Artikel zur Liste hinzufügen Artikel zur Liste hinzufügen
</li> </li>
</ul> </ul>