mirror of https://github.com/buster-so/buster.git
workspace permissions
This commit is contained in:
parent
73d68fa27f
commit
beb332f01a
|
@ -1,4 +1,4 @@
|
||||||
import { type AssetPermissionCheck, checkPermission } from '@buster/access-controls';
|
import { hasAssetPermission } from '@buster/access-controls';
|
||||||
import { executeMetricQuery, getCachedMetricData, setCachedMetricData } from '@buster/data-source';
|
import { executeMetricQuery, getCachedMetricData, setCachedMetricData } from '@buster/data-source';
|
||||||
import type { Credentials } from '@buster/data-source';
|
import type { Credentials } from '@buster/data-source';
|
||||||
import type { User } from '@buster/database';
|
import type { User } from '@buster/database';
|
||||||
|
@ -49,18 +49,38 @@ export async function getMetricDataHandler(
|
||||||
|
|
||||||
const { organizationId } = userOrg;
|
const { organizationId } = userOrg;
|
||||||
|
|
||||||
|
// Retrieve metric definition from database with data source info
|
||||||
|
const metric = await getMetricWithDataSource({ metricId, versionNumber });
|
||||||
|
|
||||||
|
if (!metric) {
|
||||||
|
throw new HTTPException(404, {
|
||||||
|
message: 'Metric not found',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify metric belongs to user's organization
|
||||||
|
if (metric.organizationId !== organizationId) {
|
||||||
|
throw new HTTPException(403, {
|
||||||
|
message: 'You do not have permission to view this metric',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Check if user has permission to view this metric file
|
// Check if user has permission to view this metric file
|
||||||
const permissionCheck: AssetPermissionCheck = {
|
// This follows the same pattern as report-files.ts - hasAssetPermission handles all the logic including:
|
||||||
|
// - Direct permissions
|
||||||
|
// - Workspace sharing permissions
|
||||||
|
// - Cascading permissions (dashboard, chat, collection)
|
||||||
|
// - Admin permissions
|
||||||
|
const hasAccess = await hasAssetPermission({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
assetId: metricId,
|
assetId: metricId,
|
||||||
assetType: 'metric_file',
|
assetType: 'metric_file',
|
||||||
requiredRole: 'can_view',
|
requiredRole: 'can_view',
|
||||||
organizationId,
|
organizationId,
|
||||||
};
|
workspaceSharing: metric.workspaceSharing ?? 'none',
|
||||||
|
});
|
||||||
|
|
||||||
const permissionResult = await checkPermission(permissionCheck);
|
if (!hasAccess) {
|
||||||
|
|
||||||
if (!permissionResult.hasAccess) {
|
|
||||||
throw new HTTPException(403, {
|
throw new HTTPException(403, {
|
||||||
message: 'You do not have permission to view this metric',
|
message: 'You do not have permission to view this metric',
|
||||||
});
|
});
|
||||||
|
@ -100,22 +120,6 @@ export async function getMetricDataHandler(
|
||||||
// Ensure limit is within bounds
|
// Ensure limit is within bounds
|
||||||
const queryLimit = Math.min(Math.max(limit, 1), 5000);
|
const queryLimit = Math.min(Math.max(limit, 1), 5000);
|
||||||
|
|
||||||
// Retrieve metric definition from database with data source info
|
|
||||||
const metric = await getMetricWithDataSource({ metricId, versionNumber });
|
|
||||||
|
|
||||||
if (!metric) {
|
|
||||||
throw new HTTPException(404, {
|
|
||||||
message: 'Metric not found',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify metric belongs to user's organization
|
|
||||||
if (metric.organizationId !== organizationId) {
|
|
||||||
throw new HTTPException(403, {
|
|
||||||
message: 'You do not have permission to view this metric',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract SQL query from metric content
|
// Extract SQL query from metric content
|
||||||
const sql = extractSqlFromMetricContent(metric.content);
|
const sql = extractSqlFromMetricContent(metric.content);
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,14 @@ export {
|
||||||
listPermissions,
|
listPermissions,
|
||||||
// From checks.ts
|
// From checks.ts
|
||||||
checkPermission,
|
checkPermission,
|
||||||
|
computeEffectivePermission,
|
||||||
type AssetPermissionCheck,
|
type AssetPermissionCheck,
|
||||||
type AssetPermissionResult,
|
type AssetPermissionResult,
|
||||||
// From cascading-permissions.ts
|
// From cascading-permissions.ts
|
||||||
checkCascadingPermissions,
|
checkCascadingPermissions,
|
||||||
|
checkMetricDashboardAccess,
|
||||||
|
checkMetricChatAccess,
|
||||||
|
checkMetricCollectionAccess,
|
||||||
} from './assets';
|
} from './assets';
|
||||||
|
|
||||||
// Export dataset permissions
|
// Export dataset permissions
|
||||||
|
|
|
@ -43,6 +43,13 @@ export const MetricWithDataSourceSchema = z.object({
|
||||||
secretId: z.string(),
|
secretId: z.string(),
|
||||||
dataSourceType: z.string(),
|
dataSourceType: z.string(),
|
||||||
versionNumber: z.number().optional(),
|
versionNumber: z.number().optional(),
|
||||||
|
workspaceSharing: z.enum(['none', 'can_view', 'can_edit', 'full_access']).nullable(),
|
||||||
|
workspaceSharingEnabledBy: z.string().nullable(),
|
||||||
|
workspaceSharingEnabledAt: z.string().nullable(),
|
||||||
|
publiclyAccessible: z.boolean(),
|
||||||
|
publiclyEnabledBy: z.string().nullable(),
|
||||||
|
publicExpiryDate: z.string().nullable(),
|
||||||
|
publicPassword: z.string().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type MetricWithDataSource = z.infer<typeof MetricWithDataSourceSchema>;
|
export type MetricWithDataSource = z.infer<typeof MetricWithDataSourceSchema>;
|
||||||
|
@ -68,6 +75,13 @@ export async function getMetricWithDataSource(
|
||||||
versionHistory: metricFiles.versionHistory,
|
versionHistory: metricFiles.versionHistory,
|
||||||
secretId: dataSources.secretId,
|
secretId: dataSources.secretId,
|
||||||
dataSourceType: dataSources.type,
|
dataSourceType: dataSources.type,
|
||||||
|
workspaceSharing: metricFiles.workspaceSharing,
|
||||||
|
workspaceSharingEnabledBy: metricFiles.workspaceSharingEnabledBy,
|
||||||
|
workspaceSharingEnabledAt: metricFiles.workspaceSharingEnabledAt,
|
||||||
|
publiclyAccessible: metricFiles.publiclyAccessible,
|
||||||
|
publiclyEnabledBy: metricFiles.publiclyEnabledBy,
|
||||||
|
publicExpiryDate: metricFiles.publicExpiryDate,
|
||||||
|
publicPassword: metricFiles.publicPassword,
|
||||||
})
|
})
|
||||||
.from(metricFiles)
|
.from(metricFiles)
|
||||||
.innerJoin(dataSources, eq(metricFiles.dataSourceId, dataSources.id))
|
.innerJoin(dataSources, eq(metricFiles.dataSourceId, dataSources.id))
|
||||||
|
@ -134,6 +148,13 @@ export async function getMetricWithDataSource(
|
||||||
versionHistory,
|
versionHistory,
|
||||||
secretId: result.secretId,
|
secretId: result.secretId,
|
||||||
dataSourceType: result.dataSourceType,
|
dataSourceType: result.dataSourceType,
|
||||||
|
workspaceSharing: result.workspaceSharing,
|
||||||
|
workspaceSharingEnabledBy: result.workspaceSharingEnabledBy,
|
||||||
|
workspaceSharingEnabledAt: result.workspaceSharingEnabledAt,
|
||||||
|
publiclyAccessible: result.publiclyAccessible,
|
||||||
|
publiclyEnabledBy: result.publiclyEnabledBy,
|
||||||
|
publicExpiryDate: result.publicExpiryDate,
|
||||||
|
publicPassword: result.publicPassword,
|
||||||
...(versionNumber !== undefined && { versionNumber }),
|
...(versionNumber !== undefined && { versionNumber }),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue