must be org member

This commit is contained in:
Nate Kelley 2025-09-20 13:49:20 -06:00
parent f406dd5ca3
commit 3bff20c8e0
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
8 changed files with 71 additions and 41 deletions

View File

@ -40,14 +40,7 @@ export const requireAuth = bearerAuth({
c.set('busterUser', busterUser);
// Ensure user is not anonymous
const isAuthenticated = !data.user.is_anonymous;
if (!isAuthenticated) {
console.info('Anonymous user attempted to access protected resource');
}
return isAuthenticated;
return true;
} catch (error) {
console.error('Unexpected error during token validation:', error);
return false;

View File

@ -1,19 +1,23 @@
import type { AssetType } from '@buster/server-shared/assets';
import type { QueryClient } from '@tanstack/react-query';
import { prefetchGetMetric } from '@/api/buster_rest/metrics';
import { useGetMetricParams } from '@/context/Metrics/useGetMetricParams';
import { MetricViewChartController } from '@/controllers/MetricController/MetricViewChartController';
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
export const ssr = true;
export const component = () => {
const { metricId } = useGetMetricParams();
return (
<AppAssetCheckLayout assetType={'metric_file'}>
<MetricViewChartController
metricId={metricId}
className="h-full w-full"
cardClassName="max-h-full!"
readOnly
/>
</AppAssetCheckLayout>
);
};
@ -38,3 +42,7 @@ export const loader = async ({
title: metric?.name,
};
};
export const staticData = {
assetType: 'metric_file' as AssetType,
};

View File

@ -48,6 +48,17 @@ const getAssetAccess = (
};
}
// 403 is no access
if (error?.status === 403) {
return {
hasAccess: false,
passwordRequired: false,
isPublic: false,
isDeleted: false,
isFetched,
};
}
return {
hasAccess: true,
passwordRequired: false,
@ -86,7 +97,7 @@ export const useGetAssetPasswordConfig = (
return chatQueryKeys.chatsGetChat(assetId);
}, [type, assetId, chosenVersionNumber]);
const { error, isFetched } = useQuery({
const { error, isFetched, data } = useQuery({
queryKey: selectedQuery.queryKey,
enabled: true,
select: useCallback((v: unknown) => !!v, []),

View File

@ -10,21 +10,17 @@ export const Route = createFileRoute('/app/_app/_asset')({
beforeLoad: async ({ matches }) => {
const assetType = [...matches].reverse().find(({ staticData }) => staticData?.assetType)
?.staticData?.assetType as AssetType;
return {
assetType,
};
return { assetType };
},
loader: async ({ context }) => {
const { assetType } = context;
return {
assetType,
};
return { assetType };
},
});
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
function RouteComponent() {
const assetType = Route.useRouteContext({ select: stableCtxSelector }) || 'metric_file';
const assetType = Route.useLoaderData({ select: stableCtxSelector }) || 'metric_file';
return (
<AppAssetCheckLayout assetType={assetType}>

View File

@ -1,15 +1,41 @@
import { createFileRoute, Outlet } from '@tanstack/react-router';
import type { AssetType } from '@buster/server-shared/assets';
import { createFileRoute, Outlet, type RouteContext } from '@tanstack/react-router';
import { prefetchGetMyUserInfo } from '@/api/buster_rest/users';
import { signInWithAnonymousUser } from '@/integrations/supabase/signIn';
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
export const Route = createFileRoute('/embed')({
beforeLoad: async ({ context }) => {
beforeLoad: async ({ context, matches }) => {
const user = await prefetchGetMyUserInfo(context.queryClient);
if (!user) await signInWithAnonymousUser(); //we fallback to an anonymous user
const assetType = [...matches].reverse().find(({ staticData }) => staticData?.assetType)
?.staticData?.assetType as AssetType;
return {
assetType,
};
},
loader: async ({ context }) => {
const { assetType } = context;
return { assetType };
},
component: RouteComponent,
ssr: false,
});
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
function RouteComponent() {
return <Outlet />;
const assetType = Route.useLoaderData({ select: stableCtxSelector });
if (!assetType) {
return (
<div className="flex h-full w-full items-center justify-center">No asset type found</div>
);
}
return (
<AppAssetCheckLayout assetType={assetType}>
<Outlet />
</AppAssetCheckLayout>
);
}

View File

@ -23,6 +23,9 @@ export const Route = createFileRoute('/embed/dashboard/$dashboardId')({
};
},
component: RouteComponent,
staticData: {
assetType: 'dashboard_file',
},
ssr: true,
});

View File

@ -30,6 +30,9 @@ export const Route = createFileRoute('/embed/report/$reportId')({
],
};
},
staticData: {
assetType: 'report_file',
},
});
function RouteComponent() {

View File

@ -8,7 +8,6 @@ import {
reportFiles,
users,
} from '../../schema';
import { AssetPermissionRoleSchema } from '../../schema-types';
import { getAssetPermission } from '../assets';
import { getOrganizationMemberCount, getUserOrganizationId } from '../organizations';
@ -27,11 +26,8 @@ export async function getReportFileById(input: GetReportInput) {
const userOrg = await getUserOrganizationId(userId);
if (!userOrg?.organizationId) {
throw new Error('User not found in any organization');
}
const { organizationId } = userOrg;
const organizationId = userOrg?.organizationId || '';
const isOrganizationMember = organizationId !== '';
const reportCollectionsQuery = db
.select({
@ -73,13 +69,7 @@ export async function getReportFileById(input: GetReportInput) {
})
.from(reportFiles)
.innerJoin(users, eq(reportFiles.createdBy, users.id))
.where(
and(
eq(reportFiles.id, reportId),
eq(reportFiles.organizationId, organizationId),
isNull(reportFiles.deletedAt)
)
)
.where(and(eq(reportFiles.id, reportId), isNull(reportFiles.deletedAt)))
.limit(1);
// Individual permissions query - get users with direct permissions to this report
@ -111,9 +101,9 @@ export async function getReportFileById(input: GetReportInput) {
userPermission,
] = await Promise.all([
reportDataQuery,
reportCollectionsQuery,
individualPermissionsQuery,
getOrganizationMemberCount(organizationId),
isOrganizationMember ? reportCollectionsQuery : Promise.resolve([]),
isOrganizationMember ? individualPermissionsQuery : Promise.resolve([]),
isOrganizationMember ? getOrganizationMemberCount(organizationId) : Promise.resolve(0),
getAssetPermission(userId, reportId, 'report_file'),
]);
const reportData = reportDataResult[0];