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:
@@ -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
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|||||||
@@ -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 ?? ''}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user