fix(ui): hide write/edit controls from READ_ALL (read-only) users (#696) #699
@@ -297,6 +297,13 @@ class DocumentControllerTest {
|
|||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "READ_ALL")
|
||||||
|
void createDocument_returns403_forReaderOnly() throws Exception {
|
||||||
|
mockMvc.perform(multipart("/api/documents").with(csrf()))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser(authorities = "WRITE_ALL")
|
@WithMockUser(authorities = "WRITE_ALL")
|
||||||
void createDocument_returns200_whenHasWritePermission() throws Exception {
|
void createDocument_returns200_whenHasWritePermission() throws Exception {
|
||||||
@@ -414,6 +421,13 @@ class DocumentControllerTest {
|
|||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithMockUser(authorities = "READ_ALL")
|
||||||
|
void quickUpload_returns403_forReaderOnly() throws Exception {
|
||||||
|
mockMvc.perform(multipart("/api/documents/quick-upload").with(csrf()))
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithMockUser(authorities = "WRITE_ALL")
|
@WithMockUser(authorities = "WRITE_ALL")
|
||||||
void quickUpload_returns200_withValidPdfFile() throws Exception {
|
void quickUpload_returns200_withValidPdfFile() throws Exception {
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ const isAdmin = $derived(
|
|||||||
data?.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'))
|
data?.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const canUpload = $derived(Boolean(data?.user && data.canWrite));
|
||||||
|
|
||||||
// Set after client-side hydration completes. Used by E2E tests to know the
|
// Set after client-side hydration completes. Used by E2E tests to know the
|
||||||
// page is interactive (event handlers registered) before they interact with it.
|
// page is interactive (event handlers registered) before they interact with it.
|
||||||
let hydrated = $state(false);
|
let hydrated = $state(false);
|
||||||
@@ -75,7 +77,7 @@ const userInitials = $derived.by(() => {
|
|||||||
|
|
||||||
<!-- Right Side -->
|
<!-- Right Side -->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
{#if data?.user}
|
{#if canUpload}
|
||||||
<a
|
<a
|
||||||
href="/documents/new"
|
href="/documents/new"
|
||||||
aria-label={m.upload_action()}
|
aria-label={m.upload_action()}
|
||||||
|
|||||||
@@ -83,6 +83,23 @@ describe('Layout – upload link', () => {
|
|||||||
const link = page.getByRole('link', { name: /Hochladen|Upload|Subir/i });
|
const link = page.getByRole('link', { name: /Hochladen|Upload|Subir/i });
|
||||||
await expect.element(link).toHaveAttribute('href', '/documents/new');
|
await expect.element(link).toHaveAttribute('href', '/documents/new');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('is hidden for a user without WRITE_ALL', async () => {
|
||||||
|
render(Layout, { data: makeData({ canWrite: false }), children: emptySnippet });
|
||||||
|
await expect
|
||||||
|
.element(page.getByRole('link', { name: /Hochladen|Upload|Subir/i }))
|
||||||
|
.not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is hidden for an ANNOTATE_ALL-only user (gate is lack of WRITE_ALL, not READ_ALL)', async () => {
|
||||||
|
render(Layout, {
|
||||||
|
data: makeData({ canWrite: false, canAnnotate: true }),
|
||||||
|
children: emptySnippet
|
||||||
|
});
|
||||||
|
await expect
|
||||||
|
.element(page.getByRole('link', { name: /Hochladen|Upload|Subir/i }))
|
||||||
|
.not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Dropdown ─────────────────────────────────────────────────────────────────
|
// ─── Dropdown ─────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user