-
-
+
+
+
+
diff --git a/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte b/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte
index 2689e031..e1e1c504 100644
--- a/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte
+++ b/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte
@@ -102,7 +102,8 @@
logo: $inputs.logoUrl?.value ? undefined : logo,
logoUrl: $inputs.logoUrl?.value,
darkLogo: $inputs.darkLogoUrl?.value ? undefined : darkLogo,
- darkLogoUrl: $inputs.darkLogoUrl?.value
+ darkLogoUrl: $inputs.darkLogoUrl?.value,
+ isGroupRestricted: existingClient?.isGroupRestricted ?? true
});
const hasLogo = logo != null || !!$inputs.logoUrl?.value;
diff --git a/frontend/src/routes/settings/admin/user-groups/[id]/+page.svelte b/frontend/src/routes/settings/admin/user-groups/[id]/+page.svelte
index bb44ff1a..768285c5 100644
--- a/frontend/src/routes/settings/admin/user-groups/[id]/+page.svelte
+++ b/frontend/src/routes/settings/admin/user-groups/[id]/+page.svelte
@@ -15,11 +15,13 @@
import { backNavigate } from '../../users/navigate-back-util';
import UserGroupForm from '../user-group-form.svelte';
import UserSelection from '../user-selection.svelte';
+ import OidcClientSelection from './oidc-client-selection.svelte';
let { data } = $props();
let userGroup = $state({
...data.userGroup,
- userIds: data.userGroup.users.map((u) => u.id)
+ userIds: data.userGroup.users.map((u) => u.id),
+ allowedOidcClientIds: data.userGroup.allowedOidcClients.map((c) => c.id)
});
const userGroupService = new UserGroupService();
@@ -56,6 +58,17 @@
axiosErrorToast(e);
});
}
+
+ async function updateAllowedOidcClients(allowedClients: string[]) {
+ await userGroupService
+ .updateAllowedOidcClients(userGroup.id, allowedClients)
+ .then(() => {
+ toast.success(m.allowed_oidc_clients_updated_successfully());
+ })
+ .catch((e) => {
+ axiosErrorToast(e);
+ });
+ }
@@ -110,3 +123,16 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/routes/settings/admin/user-groups/[id]/oidc-client-selection.svelte b/frontend/src/routes/settings/admin/user-groups/[id]/oidc-client-selection.svelte
new file mode 100644
index 00000000..552bb713
--- /dev/null
+++ b/frontend/src/routes/settings/admin/user-groups/[id]/oidc-client-selection.svelte
@@ -0,0 +1,69 @@
+
+
+{#snippet LogoCell({ item }: { item: OidcClient })}
+ {#if item.hasLogo}
+
+ {:else}
+
+ {item.name.charAt(0).toUpperCase()}
+
+ {/if}
+{/snippet}
+
+
!item.isGroupRestricted}
+ {columns}
+/>
diff --git a/frontend/src/routes/settings/admin/user-groups/user-group-list.svelte b/frontend/src/routes/settings/admin/user-groups/user-group-list.svelte
index 59f359df..812753df 100644
--- a/frontend/src/routes/settings/admin/user-groups/user-group-list.svelte
+++ b/frontend/src/routes/settings/admin/user-groups/user-group-list.svelte
@@ -10,19 +10,19 @@
AdvancedTableColumn,
CreateAdvancedTableActions
} from '$lib/types/advanced-table.type';
- import type { UserGroup, UserGroupWithUserCount } from '$lib/types/user-group.type';
+ import type { UserGroupMinimal } from '$lib/types/user-group.type';
import { axiosErrorToast } from '$lib/utils/error-util';
import { LucidePencil, LucideTrash } from '@lucide/svelte';
import { toast } from 'svelte-sonner';
const userGroupService = new UserGroupService();
- let tableRef: AdvancedTable;
+ let tableRef: AdvancedTable;
export function refresh() {
return tableRef?.refresh();
}
- const columns: AdvancedTableColumn[] = [
+ const columns: AdvancedTableColumn[] = [
{ label: 'ID', column: 'id', hidden: true },
{ label: m.friendly_name(), column: 'friendlyName', sortable: true },
{ label: m.name(), column: 'name', sortable: true },
@@ -38,7 +38,7 @@
{ label: m.source(), key: 'source', hidden: !$appConfigStore.ldapEnabled, cell: SourceCell }
];
- const actions: CreateAdvancedTableActions = (group) => [
+ const actions: CreateAdvancedTableActions = (group) => [
{
label: m.edit(),
primary: true,
@@ -55,7 +55,7 @@
}
];
- async function deleteUserGroup(userGroup: UserGroup) {
+ async function deleteUserGroup(userGroup: UserGroupMinimal) {
openConfirmDialog({
title: m.delete_name({ name: userGroup.name }),
message: m.are_you_sure_you_want_to_delete_this_user_group(),
@@ -76,7 +76,7 @@
}
-{#snippet SourceCell({ item }: { item: UserGroupWithUserCount })}
+{#snippet SourceCell({ item }: { item: UserGroupMinimal })}
{item.ldapId ? m.ldap() : m.local()}
diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js
index 584c6315..1bd67d78 100644
--- a/frontend/svelte.config.js
+++ b/frontend/svelte.config.js
@@ -7,7 +7,13 @@ const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
-
+ compilerOptions: {
+ warningFilter: (warning) => {
+ // Ignore "state_referenced_locally" warnings
+ if (warning.code === 'state_referenced_locally') return false;
+ return true;
+ }
+ },
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
diff --git a/tests/specs/oidc-client-settings.spec.ts b/tests/specs/oidc-client-settings.spec.ts
index 5bcd31a2..5c25c0e2 100644
--- a/tests/specs/oidc-client-settings.spec.ts
+++ b/tests/specs/oidc-client-settings.spec.ts
@@ -1,5 +1,5 @@
import test, { expect, Page } from '@playwright/test';
-import { oidcClients } from '../data';
+import { oidcClients, userGroups } from '../data';
import { cleanupBackend } from '../utils/cleanup.util';
test.beforeEach(async () => await cleanupBackend());
@@ -117,3 +117,25 @@ test('Delete OIDC client', async ({ page }) => {
);
await expect(page.getByRole('row', { name: oidcClient.name })).not.toBeVisible();
});
+
+test('Update OIDC client allowed user groups', async ({ page }) => {
+ await page.goto(`/settings/admin/oidc-clients/${oidcClients.nextcloud.id}`);
+
+ await page.getByRole('button', { name: 'Restrict' }).click();
+
+ await page.getByRole('row', { name: userGroups.designers.name }).getByRole('checkbox').click();
+ await page.getByRole('row', { name: userGroups.developers.name }).getByRole('checkbox').click();
+
+ await page.getByRole('button', { name: 'Save' }).nth(1).click();
+
+ await expect(page.getByText('Allowed user groups updated successfully')).toBeVisible();
+
+ await page.reload();
+
+ await expect(
+ page.getByRole('row', { name: userGroups.designers.name }).getByRole('checkbox')
+ ).toHaveAttribute('data-state', 'checked');
+ await expect(
+ page.getByRole('row', { name: userGroups.developers.name }).getByRole('checkbox')
+ ).toHaveAttribute('data-state', 'checked');
+});
diff --git a/tests/specs/user-group.spec.ts b/tests/specs/user-group.spec.ts
index 7deb39b0..96f2ef71 100644
--- a/tests/specs/user-group.spec.ts
+++ b/tests/specs/user-group.spec.ts
@@ -1,5 +1,5 @@
import test, { expect } from '@playwright/test';
-import { userGroups, users } from '../data';
+import { oidcClients, userGroups, users } from '../data';
import { cleanupBackend } from '../utils/cleanup.util';
test.beforeEach(async () => await cleanupBackend());
@@ -77,7 +77,7 @@ test('Delete user group', async ({ page }) => {
test('Update user group custom claims', async ({ page }) => {
await page.goto(`/settings/admin/user-groups/${userGroups.designers.id}`);
- await page.getByRole('button', { name: 'Expand card' }).click();
+ await page.getByRole('button', { name: 'Expand card' }).first().click();
// Add two custom claims
await page.getByRole('button', { name: 'Add custom claim' }).click();
@@ -119,3 +119,34 @@ test('Update user group custom claims', async ({ page }) => {
await expect(page.getByPlaceholder('Key').first()).toHaveValue('customClaim2');
await expect(page.getByPlaceholder('Value').first()).toHaveValue('customClaim2_value');
});
+
+test('Update user group allowed user groups', async ({ page }) => {
+ await page.goto(`/settings/admin/user-groups/${userGroups.designers.id}`);
+
+ await page.getByRole('button', { name: 'Expand card' }).nth(1).click();
+
+ // Unrestricted OIDC clients should be checked and disabled
+ const nextcloudRow = page
+ .getByRole('row', { name: oidcClients.nextcloud.name })
+ .getByRole('checkbox');
+ await expect(nextcloudRow).toHaveAttribute('data-state', 'checked');
+ await expect(nextcloudRow).toBeDisabled();
+
+ await page.getByRole('row', { name: oidcClients.tailscale.name }).getByRole('checkbox').click();
+ await page.getByRole('row', { name: oidcClients.immich.name }).getByRole('checkbox').click();
+
+ await page.getByRole('button', { name: 'Save' }).nth(2).click();
+
+ await expect(page.locator('[data-type="success"]')).toHaveText(
+ 'Allowed OIDC clients updated successfully'
+ );
+
+ await page.reload();
+
+ await expect(
+ page.getByRole('row', { name: oidcClients.tailscale.name }).getByRole('checkbox')
+ ).toHaveAttribute('data-state', 'checked');
+ await expect(
+ page.getByRole('row', { name: oidcClients.immich.name }).getByRole('checkbox')
+ ).toHaveAttribute('data-state', 'unchecked');
+});