Skip to content

Commit b043ed8

Browse files
feat: use server-side permissions for View As UI gating
Use the existing GetProjectWithBearerToken pattern to fetch the impersonated user's projectPermissions from the server, rather than reconstructing permissions client-side. Changes: - Project layout calls GetProjectWithBearerToken with the mocked user's JWT (from GetDeploymentCredentials) to get their actual permissions - Created effectivePermissionsStore to share permissions with TopNavBar - ProjectTabs uses effectiveProjectPermissions (impersonated user's permissions when View As is active) - TopNavigationBar uses effective permissions for Share button visibility This follows the existing architecture where the server is the single source of truth for permissions, rather than duplicating permission logic on the client. Co-authored-by: ericokuma <ericokuma@users.noreply.github.com>
1 parent 3fc0678 commit b043ed8

3 files changed

Lines changed: 60 additions & 3 deletions

File tree

web-admin/src/features/navigation/TopNavigationBar.svelte

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
} from "../../client";
2222
import ViewAsUserChip from "../../features/view-as-user/ViewAsUserChip.svelte";
2323
import { viewAsUserStore } from "../../features/view-as-user/viewAsUserStore";
24+
import { effectiveProjectPermissionsStore } from "../../features/view-as-user/effectivePermissionsStore";
2425
import CreateAlert from "../alerts/CreateAlert.svelte";
2526
import { useAlerts } from "../alerts/selectors";
2627
import AvatarButton from "../authentication/AvatarButton.svelte";
@@ -69,6 +70,15 @@
6970
$: onPublicURLPage = isPublicURLPage($page);
7071
$: onOrgPage = isOrganizationPage($page);
7172
73+
// Use effective permissions when "View As" is active (from server)
74+
// Otherwise fall back to the props passed from the root layout
75+
$: effectiveManageProjectMembers =
76+
$effectiveProjectPermissionsStore?.manageProjectMembers ??
77+
manageProjectMembers;
78+
$: effectiveCreateMagicAuthTokens =
79+
$effectiveProjectPermissionsStore?.createMagicAuthTokens ??
80+
createMagicAuthTokens;
81+
7282
$: loggedIn = !!$user.data?.user;
7383
$: rillLogoHref = !loggedIn ? "https://www.rilldata.com" : "/";
7484
$: logoUrl = organizationLogoUrl;
@@ -231,7 +241,7 @@
231241
{/if}
232242
<!-- NOTE: only project admin and editor can manage project members -->
233243
<!-- https://docs.rilldata.com/guide/administration/users-and-access/roles-permissions -->
234-
{#if onProjectPage && manageProjectMembers}
244+
{#if onProjectPage && effectiveManageProjectMembers}
235245
<ShareProjectPopover
236246
{organization}
237247
{project}
@@ -264,7 +274,9 @@
264274
{#if $alertsFlag}
265275
<CreateAlert />
266276
{/if}
267-
<ShareDashboardPopover {createMagicAuthTokens} />
277+
<ShareDashboardPopover
278+
createMagicAuthTokens={effectiveCreateMagicAuthTokens}
279+
/>
268280
{/if}
269281
</StateManagersProvider>
270282
{/key}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { writable } from "svelte/store";
2+
import type { V1ProjectPermissions } from "../../client";
3+
4+
/**
5+
* Store for effective project permissions when "View As" is active.
6+
* When null, the actual user's permissions should be used.
7+
* When set, these are the impersonated user's permissions (from server).
8+
*/
9+
export const effectiveProjectPermissionsStore =
10+
writable<V1ProjectPermissions | null>(null);

web-admin/src/routes/[organization]/[project]/+layout.svelte

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import { createAdminServiceGetProjectWithBearerToken } from "@rilldata/web-admin/features/public-urls/get-project-with-bearer-token";
4949
import { cloudVersion } from "@rilldata/web-admin/features/telemetry/initCloudMetrics";
5050
import { viewAsUserStore } from "@rilldata/web-admin/features/view-as-user/viewAsUserStore";
51+
import { effectiveProjectPermissionsStore } from "@rilldata/web-admin/features/view-as-user/effectivePermissionsStore";
5152
import ErrorPage from "@rilldata/web-common/components/ErrorPage.svelte";
5253
import { metricsService } from "@rilldata/web-common/metrics/initMetrics";
5354
import RuntimeProvider from "@rilldata/web-common/runtime-client/RuntimeProvider.svelte";
@@ -122,7 +123,41 @@
122123
$: ({ data: mockedUserDeploymentCredentials } =
123124
$mockedUserDeploymentCredentialsQuery);
124125
126+
/**
127+
* When "View As" is active, fetch the project using the mocked user's JWT.
128+
* This returns the impersonated user's `projectPermissions` from the server.
129+
*/
130+
$: mockedUserProjectQuery = createAdminServiceGetProjectWithBearerToken(
131+
organization,
132+
project,
133+
mockedUserDeploymentCredentials?.accessToken ?? "",
134+
undefined,
135+
{
136+
query: {
137+
enabled: !!mockedUserDeploymentCredentials?.accessToken,
138+
},
139+
},
140+
);
141+
125142
$: ({ data: projectData, error: projectError } = $projectQuery);
143+
144+
/**
145+
* Compute effective project permissions.
146+
* When "View As" is active, use the impersonated user's permissions (from server).
147+
* Otherwise, use the actual user's permissions.
148+
*/
149+
$: effectiveProjectPermissions =
150+
mockedUserId && $mockedUserProjectQuery.data?.projectPermissions
151+
? $mockedUserProjectQuery.data.projectPermissions
152+
: projectData?.projectPermissions;
153+
154+
// Update the global store so TopNavigationBar can access effective permissions
155+
$: effectiveProjectPermissionsStore.set(
156+
mockedUserId && $mockedUserProjectQuery.data?.projectPermissions
157+
? $mockedUserProjectQuery.data.projectPermissions
158+
: null,
159+
);
160+
126161
$: deploymentStatus = projectData?.deployment?.status;
127162
// A re-deploy triggers `DEPLOYMENT_STATUS_UPDATING` status. But we can still show the project UI.
128163
$: isProjectAvailable =
@@ -168,7 +203,7 @@
168203

169204
{#if onProjectPage && deploymentStatus === V1DeploymentStatus.DEPLOYMENT_STATUS_RUNNING}
170205
<ProjectTabs
171-
projectPermissions={projectData.projectPermissions}
206+
projectPermissions={effectiveProjectPermissions}
172207
{organization}
173208
{pathname}
174209
{project}

0 commit comments

Comments
 (0)