-
Notifications
You must be signed in to change notification settings - Fork 124
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
aac65ad
commit 770d813
Showing
7 changed files
with
273 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
web-admin/src/components/authentication/ViewAsUserChip.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<script lang="ts"> | ||
import { page } from "$app/stores"; | ||
import { | ||
Popover, | ||
PopoverButton, | ||
PopoverPanel, | ||
} from "@rgossiaux/svelte-headlessui"; | ||
import { IconSpaceFixer } from "@rilldata/web-common/components/button"; | ||
import { Chip } from "@rilldata/web-common/components/chip"; | ||
import CaretDownIcon from "@rilldata/web-common/components/icons/CaretDownIcon.svelte"; | ||
import { createPopperActions } from "svelte-popperjs"; | ||
import ViewAsUserPopover from "./ViewAsUserPopover.svelte"; | ||
import { viewAsUserStore } from "./viewAsUserStore"; | ||
// Position the popover | ||
const [popperRef, popperContent] = createPopperActions(); | ||
const popperOptions = { | ||
placement: "bottom-start", | ||
strategy: "fixed", | ||
modifiers: [{ name: "offset", options: { offset: [0, 4] } }], | ||
}; | ||
</script> | ||
|
||
<Popover use={[popperRef]} let:close let:open> | ||
<Chip | ||
removable | ||
on:remove={() => { | ||
// updateMimickedJWT(queryClient, null); | ||
viewAsUserStore.set(null); | ||
}} | ||
active={open} | ||
> | ||
<div slot="body"> | ||
<PopoverButton> | ||
<div class="flex gap-x-2"> | ||
<div> | ||
Viewing as <span class="font-bold">{$viewAsUserStore.email}</span> | ||
</div> | ||
<div class="flex items-center"> | ||
<IconSpaceFixer pullRight> | ||
<div class="transition-transform" class:-rotate-180={open}> | ||
<CaretDownIcon size="14px" /> | ||
</div> | ||
</IconSpaceFixer> | ||
</div> | ||
</div> | ||
</PopoverButton> | ||
<PopoverPanel use={[[popperContent, popperOptions]]}> | ||
<ViewAsUserPopover | ||
organization={$page.params.organization} | ||
project={$page.params.project} | ||
on:select={() => close(undefined)} | ||
/> | ||
</PopoverPanel> | ||
</div> | ||
<svelte:fragment slot="remove-tooltip"> | ||
<slot name="remove-tooltip-content">Clear view</slot> | ||
</svelte:fragment> | ||
</Chip> | ||
</Popover> |
1 change: 1 addition & 0 deletions
1
web-admin/src/components/authentication/ViewAsUserMenuItem.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<!-- TODO: move code from `UserButton.svelte` --> |
83 changes: 83 additions & 0 deletions
83
web-admin/src/components/authentication/ViewAsUserPopover.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<script lang="ts"> | ||
import { Menu, MenuItem } from "@rilldata/web-common/components/menu"; | ||
import { Search } from "@rilldata/web-common/components/search"; | ||
import { updateMimickedJWT } from "@rilldata/web-common/features/dashboards/granular-access-policies/updateMimickedJWT"; | ||
import { useQueryClient } from "@tanstack/svelte-query"; | ||
import { matchSorter } from "match-sorter"; | ||
import { createEventDispatcher } from "svelte"; | ||
import { createAdminServiceSearchProjectUsers, V1User } from "../../client"; | ||
import { viewAsUserStore } from "./viewAsUserStore"; | ||
export let organization: string; | ||
export let project: string; | ||
// Note: this will break down if there are more than 1000 users in a project | ||
$: projectUsers = createAdminServiceSearchProjectUsers( | ||
organization, | ||
project, | ||
{ emailQuery: "%", pageSize: 1000, pageToken: undefined } | ||
); | ||
const dispatch = createEventDispatcher(); | ||
const queryClient = useQueryClient(); | ||
function viewAsUser(user: V1User) { | ||
viewAsUserStore.set(user); | ||
updateMimickedJWT(queryClient, organization, project, user); | ||
dispatch("select"); | ||
} | ||
let minWidth = "150px"; | ||
let maxWidth = "300px"; | ||
let minHeight = "150px"; | ||
let maxHeight = "190px"; | ||
let searchText = ""; | ||
$: clientSideUsers = $projectUsers.data?.users ?? []; | ||
// For testing: repeat the users array X times | ||
// $: clientSideUsers = | ||
// $projectUsers.data?.users.flatMap((users) => | ||
// Array.from({ length: 10 }, () => users) | ||
// ) ?? []; | ||
$: visibleUsers = searchText | ||
? matchSorter(clientSideUsers, searchText, { keys: ["email"] }) | ||
: clientSideUsers; | ||
</script> | ||
|
||
<Menu | ||
focusOnMount={false} | ||
{minWidth} | ||
{maxWidth} | ||
{minHeight} | ||
{maxHeight} | ||
paddingBottom={0} | ||
paddingTop={1} | ||
rounded={false} | ||
on:click-outside | ||
on:escape | ||
> | ||
<div class="px-2 pt-1 pb-2 text-[10px] text-gray-500 text-left"> | ||
Test your <a | ||
target="_blank" | ||
href="https://docs.rilldata.com/develop/security">security policies</a | ||
> by viewing this project from the perspective of another user. | ||
</div> | ||
<Search bind:value={searchText} /> | ||
{#if visibleUsers.length > 0} | ||
<div class="overflow-auto pb-1"> | ||
{#each visibleUsers as user} | ||
<MenuItem | ||
animateSelect={false} | ||
focusOnMount={false} | ||
on:select={() => viewAsUser(user)} | ||
> | ||
{user.email} | ||
</MenuItem> | ||
{/each} | ||
</div> | ||
{:else} | ||
<div class="mt-5 ui-copy-disabled text-center">no results</div> | ||
{/if} | ||
</Menu> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { writable } from "svelte/store"; | ||
import type { V1User } from "../../client"; | ||
|
||
export const viewAsUserStore = writable<V1User | null>(null); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
web-common/src/features/dashboards/granular-access-policies/updateMimickedJWT.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { | ||
adminServiceGetDeploymentCredentials, | ||
type V1User, | ||
} from "@rilldata/web-admin/client"; | ||
import { viewAsUserStore } from "@rilldata/web-admin/components/authentication/viewAsUserStore"; | ||
import { invalidateAllMetricsViews } from "@rilldata/web-common/runtime-client/invalidation"; | ||
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; | ||
import type { QueryClient } from "@tanstack/svelte-query"; | ||
import { get } from "svelte/store"; | ||
|
||
export async function updateMimickedJWT( | ||
queryClient: QueryClient, | ||
organization: string, | ||
project: string, | ||
user: V1User | null | ||
) { | ||
viewAsUserStore.set(user); | ||
let jwt: string = null; | ||
|
||
if (user !== null) { | ||
try { | ||
const jwtResp = await adminServiceGetDeploymentCredentials( | ||
organization, | ||
project, | ||
{ | ||
userId: user.id, | ||
} | ||
); | ||
jwt = jwtResp.jwt; | ||
} catch (e) { | ||
// no-op | ||
} | ||
} | ||
|
||
// selectedMockUserJWT.set(jwt); | ||
runtime.update((runtimeState) => { | ||
runtimeState.jwt = jwt; | ||
return runtimeState; | ||
}); | ||
return invalidateAllMetricsViews(queryClient, get(runtime).instanceId); | ||
} |