mirror of https://github.com/buster-so/buster.git
must be org member
This commit is contained in:
parent
f406dd5ca3
commit
3bff20c8e0
|
@ -40,14 +40,7 @@ export const requireAuth = bearerAuth({
|
||||||
|
|
||||||
c.set('busterUser', busterUser);
|
c.set('busterUser', busterUser);
|
||||||
|
|
||||||
// Ensure user is not anonymous
|
return true;
|
||||||
const isAuthenticated = !data.user.is_anonymous;
|
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
|
||||||
console.info('Anonymous user attempted to access protected resource');
|
|
||||||
}
|
|
||||||
|
|
||||||
return isAuthenticated;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Unexpected error during token validation:', error);
|
console.error('Unexpected error during token validation:', error);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
|
import type { AssetType } from '@buster/server-shared/assets';
|
||||||
import type { QueryClient } from '@tanstack/react-query';
|
import type { QueryClient } from '@tanstack/react-query';
|
||||||
import { prefetchGetMetric } from '@/api/buster_rest/metrics';
|
import { prefetchGetMetric } from '@/api/buster_rest/metrics';
|
||||||
import { useGetMetricParams } from '@/context/Metrics/useGetMetricParams';
|
import { useGetMetricParams } from '@/context/Metrics/useGetMetricParams';
|
||||||
import { MetricViewChartController } from '@/controllers/MetricController/MetricViewChartController';
|
import { MetricViewChartController } from '@/controllers/MetricController/MetricViewChartController';
|
||||||
|
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
||||||
|
|
||||||
export const ssr = true;
|
export const ssr = true;
|
||||||
|
|
||||||
export const component = () => {
|
export const component = () => {
|
||||||
const { metricId } = useGetMetricParams();
|
const { metricId } = useGetMetricParams();
|
||||||
return (
|
return (
|
||||||
<MetricViewChartController
|
<AppAssetCheckLayout assetType={'metric_file'}>
|
||||||
metricId={metricId}
|
<MetricViewChartController
|
||||||
className="h-full w-full"
|
metricId={metricId}
|
||||||
cardClassName="max-h-full!"
|
className="h-full w-full"
|
||||||
readOnly
|
cardClassName="max-h-full!"
|
||||||
/>
|
readOnly
|
||||||
|
/>
|
||||||
|
</AppAssetCheckLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,3 +42,7 @@ export const loader = async ({
|
||||||
title: metric?.name,
|
title: metric?.name,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const staticData = {
|
||||||
|
assetType: 'metric_file' as AssetType,
|
||||||
|
};
|
||||||
|
|
|
@ -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 {
|
return {
|
||||||
hasAccess: true,
|
hasAccess: true,
|
||||||
passwordRequired: false,
|
passwordRequired: false,
|
||||||
|
@ -86,7 +97,7 @@ export const useGetAssetPasswordConfig = (
|
||||||
return chatQueryKeys.chatsGetChat(assetId);
|
return chatQueryKeys.chatsGetChat(assetId);
|
||||||
}, [type, assetId, chosenVersionNumber]);
|
}, [type, assetId, chosenVersionNumber]);
|
||||||
|
|
||||||
const { error, isFetched } = useQuery({
|
const { error, isFetched, data } = useQuery({
|
||||||
queryKey: selectedQuery.queryKey,
|
queryKey: selectedQuery.queryKey,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
select: useCallback((v: unknown) => !!v, []),
|
select: useCallback((v: unknown) => !!v, []),
|
||||||
|
|
|
@ -10,21 +10,17 @@ export const Route = createFileRoute('/app/_app/_asset')({
|
||||||
beforeLoad: async ({ matches }) => {
|
beforeLoad: async ({ matches }) => {
|
||||||
const assetType = [...matches].reverse().find(({ staticData }) => staticData?.assetType)
|
const assetType = [...matches].reverse().find(({ staticData }) => staticData?.assetType)
|
||||||
?.staticData?.assetType as AssetType;
|
?.staticData?.assetType as AssetType;
|
||||||
return {
|
return { assetType };
|
||||||
assetType,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
loader: async ({ context }) => {
|
loader: async ({ context }) => {
|
||||||
const { assetType } = context;
|
const { assetType } = context;
|
||||||
return {
|
return { assetType };
|
||||||
assetType,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const assetType = Route.useRouteContext({ select: stableCtxSelector }) || 'metric_file';
|
const assetType = Route.useLoaderData({ select: stableCtxSelector }) || 'metric_file';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppAssetCheckLayout assetType={assetType}>
|
<AppAssetCheckLayout assetType={assetType}>
|
||||||
|
|
|
@ -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 { prefetchGetMyUserInfo } from '@/api/buster_rest/users';
|
||||||
import { signInWithAnonymousUser } from '@/integrations/supabase/signIn';
|
import { signInWithAnonymousUser } from '@/integrations/supabase/signIn';
|
||||||
|
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed')({
|
export const Route = createFileRoute('/embed')({
|
||||||
beforeLoad: async ({ context }) => {
|
beforeLoad: async ({ context, matches }) => {
|
||||||
const user = await prefetchGetMyUserInfo(context.queryClient);
|
const user = await prefetchGetMyUserInfo(context.queryClient);
|
||||||
if (!user) await signInWithAnonymousUser(); //we fallback to an anonymous user
|
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,
|
component: RouteComponent,
|
||||||
|
ssr: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
||||||
function RouteComponent() {
|
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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ export const Route = createFileRoute('/embed/dashboard/$dashboardId')({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
staticData: {
|
||||||
|
assetType: 'dashboard_file',
|
||||||
|
},
|
||||||
ssr: true,
|
ssr: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,9 @@ export const Route = createFileRoute('/embed/report/$reportId')({
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
staticData: {
|
||||||
|
assetType: 'report_file',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
reportFiles,
|
reportFiles,
|
||||||
users,
|
users,
|
||||||
} from '../../schema';
|
} from '../../schema';
|
||||||
import { AssetPermissionRoleSchema } from '../../schema-types';
|
|
||||||
import { getAssetPermission } from '../assets';
|
import { getAssetPermission } from '../assets';
|
||||||
import { getOrganizationMemberCount, getUserOrganizationId } from '../organizations';
|
import { getOrganizationMemberCount, getUserOrganizationId } from '../organizations';
|
||||||
|
|
||||||
|
@ -27,11 +26,8 @@ export async function getReportFileById(input: GetReportInput) {
|
||||||
|
|
||||||
const userOrg = await getUserOrganizationId(userId);
|
const userOrg = await getUserOrganizationId(userId);
|
||||||
|
|
||||||
if (!userOrg?.organizationId) {
|
const organizationId = userOrg?.organizationId || '';
|
||||||
throw new Error('User not found in any organization');
|
const isOrganizationMember = organizationId !== '';
|
||||||
}
|
|
||||||
|
|
||||||
const { organizationId } = userOrg;
|
|
||||||
|
|
||||||
const reportCollectionsQuery = db
|
const reportCollectionsQuery = db
|
||||||
.select({
|
.select({
|
||||||
|
@ -73,13 +69,7 @@ export async function getReportFileById(input: GetReportInput) {
|
||||||
})
|
})
|
||||||
.from(reportFiles)
|
.from(reportFiles)
|
||||||
.innerJoin(users, eq(reportFiles.createdBy, users.id))
|
.innerJoin(users, eq(reportFiles.createdBy, users.id))
|
||||||
.where(
|
.where(and(eq(reportFiles.id, reportId), isNull(reportFiles.deletedAt)))
|
||||||
and(
|
|
||||||
eq(reportFiles.id, reportId),
|
|
||||||
eq(reportFiles.organizationId, organizationId),
|
|
||||||
isNull(reportFiles.deletedAt)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
// Individual permissions query - get users with direct permissions to this report
|
// Individual permissions query - get users with direct permissions to this report
|
||||||
|
@ -111,9 +101,9 @@ export async function getReportFileById(input: GetReportInput) {
|
||||||
userPermission,
|
userPermission,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
reportDataQuery,
|
reportDataQuery,
|
||||||
reportCollectionsQuery,
|
isOrganizationMember ? reportCollectionsQuery : Promise.resolve([]),
|
||||||
individualPermissionsQuery,
|
isOrganizationMember ? individualPermissionsQuery : Promise.resolve([]),
|
||||||
getOrganizationMemberCount(organizationId),
|
isOrganizationMember ? getOrganizationMemberCount(organizationId) : Promise.resolve(0),
|
||||||
getAssetPermission(userId, reportId, 'report_file'),
|
getAssetPermission(userId, reportId, 'report_file'),
|
||||||
]);
|
]);
|
||||||
const reportData = reportDataResult[0];
|
const reportData = reportDataResult[0];
|
||||||
|
|
Loading…
Reference in New Issue