mirror of https://github.com/buster-so/buster.git
reset permission stuff
This commit is contained in:
parent
273008adc6
commit
3cdd0bfa57
|
@ -79,7 +79,7 @@ export const checkIfAssetIsEditable = async ({
|
||||||
assetType: AssetType;
|
assetType: AssetType;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
workspaceSharing: WorkspaceSharing;
|
workspaceSharing: WorkspaceSharing;
|
||||||
requiredRole?: AssetPermissionRole | AssetPermissionRole[];
|
requiredRole?: AssetPermissionRole;
|
||||||
}) => {
|
}) => {
|
||||||
const assetPermissionResult = await checkPermission({
|
const assetPermissionResult = await checkPermission({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
|
|
@ -33,11 +33,9 @@ export function getPermissionCacheKey(
|
||||||
userId: string,
|
userId: string,
|
||||||
assetId: string,
|
assetId: string,
|
||||||
assetType: AssetType,
|
assetType: AssetType,
|
||||||
requiredRole: AssetPermissionRole | AssetPermissionRole[]
|
requiredRole: AssetPermissionRole
|
||||||
): CacheKey {
|
): CacheKey {
|
||||||
// Normalize and sort roles for consistent cache keys
|
return `${userId}:${assetId}:${assetType}:${requiredRole}`;
|
||||||
const roles = Array.isArray(requiredRole) ? [...requiredRole].sort() : [requiredRole];
|
|
||||||
return `${userId}:${assetId}:${assetType}:${roles.join(',')}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +45,7 @@ export function getCachedPermission(
|
||||||
userId: string,
|
userId: string,
|
||||||
assetId: string,
|
assetId: string,
|
||||||
assetType: AssetType,
|
assetType: AssetType,
|
||||||
requiredRole: AssetPermissionRole | AssetPermissionRole[]
|
requiredRole: AssetPermissionRole
|
||||||
): AssetPermissionResult | undefined {
|
): AssetPermissionResult | undefined {
|
||||||
const key = getPermissionCacheKey(userId, assetId, assetType, requiredRole);
|
const key = getPermissionCacheKey(userId, assetId, assetType, requiredRole);
|
||||||
const cached = permissionCache.get(key);
|
const cached = permissionCache.get(key);
|
||||||
|
@ -68,7 +66,7 @@ export function setCachedPermission(
|
||||||
userId: string,
|
userId: string,
|
||||||
assetId: string,
|
assetId: string,
|
||||||
assetType: AssetType,
|
assetType: AssetType,
|
||||||
requiredRole: AssetPermissionRole | AssetPermissionRole[],
|
requiredRole: AssetPermissionRole,
|
||||||
result: AssetPermissionResult
|
result: AssetPermissionResult
|
||||||
): void {
|
): void {
|
||||||
const key = getPermissionCacheKey(userId, assetId, assetType, requiredRole);
|
const key = getPermissionCacheKey(userId, assetId, assetType, requiredRole);
|
||||||
|
@ -198,13 +196,18 @@ export function invalidateUser(userId: string) {
|
||||||
* Invalidate all cached entries for a user-asset combination
|
* Invalidate all cached entries for a user-asset combination
|
||||||
*/
|
*/
|
||||||
export function invalidateUserAsset(userId: string, assetId: string, assetType: AssetType) {
|
export function invalidateUserAsset(userId: string, assetId: string, assetType: AssetType) {
|
||||||
// Invalidate all permission cache entries for this user-asset combination
|
// Invalidate all permission levels for this user-asset combination
|
||||||
// Since we now support arrays, we need to invalidate all possible combinations
|
const permissionRoles: AssetPermissionRole[] = [
|
||||||
// The simplest approach is to invalidate all entries containing the user-asset-type pattern
|
'owner',
|
||||||
for (const key of Array.from(permissionCache.keys())) {
|
'full_access',
|
||||||
if (key.startsWith(`${userId}:${assetId}:${assetType}:`)) {
|
'can_edit',
|
||||||
permissionCache.delete(key);
|
'can_filter',
|
||||||
}
|
'can_view',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const role of permissionRoles) {
|
||||||
|
const key = getPermissionCacheKey(userId, assetId, assetType, role);
|
||||||
|
permissionCache.delete(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate cascading cache
|
// Invalidate cascading cache
|
||||||
|
|
|
@ -205,7 +205,7 @@ describe('Asset Permission Checks', () => {
|
||||||
'user123',
|
'user123',
|
||||||
'asset123',
|
'asset123',
|
||||||
'dashboard_file',
|
'dashboard_file',
|
||||||
['can_view'],
|
'can_view',
|
||||||
{ hasAccess: false }
|
{ hasAccess: false }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
import type { User } from '@buster/database/queries';
|
import type { User } from '@buster/database/queries';
|
||||||
import type { AssetType } from '@buster/database/schema-types';
|
import type { AssetType } from '@buster/database/schema-types';
|
||||||
import type { AssetPermissionRole, OrganizationMembership, WorkspaceSharing } from '../types';
|
import type { AssetPermissionRole, OrganizationMembership, WorkspaceSharing } from '../types';
|
||||||
import { getHighestPermission, isPermissionSufficientForAny } from '../types/asset-permissions';
|
import { getHighestPermission, isPermissionSufficient } from '../types/asset-permissions';
|
||||||
import { getCachedPermission, setCachedPermission } from './cache';
|
import { getCachedPermission, setCachedPermission } from './cache';
|
||||||
import { checkCascadingPermissions } from './cascading-permissions';
|
import { checkCascadingPermissions } from './cascading-permissions';
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export interface AssetPermissionCheck {
|
||||||
userId: string;
|
userId: string;
|
||||||
assetId: string;
|
assetId: string;
|
||||||
assetType: AssetType;
|
assetType: AssetType;
|
||||||
requiredRole: AssetPermissionRole | AssetPermissionRole[];
|
requiredRole: AssetPermissionRole;
|
||||||
organizationId?: string;
|
organizationId?: string;
|
||||||
workspaceSharing?: WorkspaceSharing;
|
workspaceSharing?: WorkspaceSharing;
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,8 @@ export interface AssetPermissionResult {
|
||||||
export async function checkPermission(check: AssetPermissionCheck): Promise<AssetPermissionResult> {
|
export async function checkPermission(check: AssetPermissionCheck): Promise<AssetPermissionResult> {
|
||||||
const { userId, assetId, assetType, requiredRole, organizationId, workspaceSharing } = check;
|
const { userId, assetId, assetType, requiredRole, organizationId, workspaceSharing } = check;
|
||||||
|
|
||||||
// Normalize requiredRole to an array for consistent handling
|
// Check cache first
|
||||||
const requiredRoles = Array.isArray(requiredRole) ? requiredRole : [requiredRole];
|
const cached = getCachedPermission(userId, assetId, assetType, requiredRole);
|
||||||
|
|
||||||
// Check cache first (using serialized array as key)
|
|
||||||
const cached = getCachedPermission(userId, assetId, assetType, requiredRoles);
|
|
||||||
|
|
||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
@ -60,8 +56,8 @@ export async function checkPermission(check: AssetPermissionCheck): Promise<Asse
|
||||||
const dbResult = await checkDbAssetPermission(dbParams);
|
const dbResult = await checkDbAssetPermission(dbParams);
|
||||||
|
|
||||||
if (dbResult.hasAccess && dbResult.role) {
|
if (dbResult.hasAccess && dbResult.role) {
|
||||||
// Check if the role is sufficient for any of the required roles
|
// Check if the role is sufficient
|
||||||
if (isPermissionSufficientForAny(dbResult.role, requiredRoles)) {
|
if (isPermissionSufficient(dbResult.role, requiredRole)) {
|
||||||
const result: AssetPermissionResult = {
|
const result: AssetPermissionResult = {
|
||||||
hasAccess: true,
|
hasAccess: true,
|
||||||
effectiveRole: dbResult.role,
|
effectiveRole: dbResult.role,
|
||||||
|
@ -69,7 +65,7 @@ export async function checkPermission(check: AssetPermissionCheck): Promise<Asse
|
||||||
if (dbResult.accessPath !== undefined) {
|
if (dbResult.accessPath !== undefined) {
|
||||||
result.accessPath = dbResult.accessPath;
|
result.accessPath = dbResult.accessPath;
|
||||||
}
|
}
|
||||||
setCachedPermission(userId, assetId, assetType, requiredRoles, result);
|
setCachedPermission(userId, assetId, assetType, requiredRole, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,21 +76,20 @@ export async function checkPermission(check: AssetPermissionCheck): Promise<Asse
|
||||||
|
|
||||||
if (isOrgMember) {
|
if (isOrgMember) {
|
||||||
const workspaceRole = mapWorkspaceSharingToRole(workspaceSharing);
|
const workspaceRole = mapWorkspaceSharingToRole(workspaceSharing);
|
||||||
if (workspaceRole && isPermissionSufficientForAny(workspaceRole, requiredRoles)) {
|
if (workspaceRole && isPermissionSufficient(workspaceRole, requiredRole)) {
|
||||||
const result = {
|
const result = {
|
||||||
hasAccess: true,
|
hasAccess: true,
|
||||||
effectiveRole: workspaceRole,
|
effectiveRole: workspaceRole,
|
||||||
accessPath: 'workspace_sharing' as const,
|
accessPath: 'workspace_sharing' as const,
|
||||||
};
|
};
|
||||||
setCachedPermission(userId, assetId, assetType, requiredRoles, result);
|
setCachedPermission(userId, assetId, assetType, requiredRole, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cascading permissions for specific asset types
|
// Check cascading permissions for specific asset types
|
||||||
// Only check if any of the required roles is 'can_view' (cascading only provides view access)
|
if (requiredRole === 'can_view') {
|
||||||
if (requiredRoles.includes('can_view')) {
|
|
||||||
// Create a user object for cascading permissions check
|
// Create a user object for cascading permissions check
|
||||||
const user: Pick<User, 'id'> = { id: userId };
|
const user: Pick<User, 'id'> = { id: userId };
|
||||||
const hasCascadingAccess = await checkCascadingPermissions(assetId, assetType, user as User);
|
const hasCascadingAccess = await checkCascadingPermissions(assetId, assetType, user as User);
|
||||||
|
@ -104,13 +99,13 @@ export async function checkPermission(check: AssetPermissionCheck): Promise<Asse
|
||||||
effectiveRole: 'can_view' as AssetPermissionRole,
|
effectiveRole: 'can_view' as AssetPermissionRole,
|
||||||
accessPath: 'cascading' as const,
|
accessPath: 'cascading' as const,
|
||||||
};
|
};
|
||||||
setCachedPermission(userId, assetId, assetType, requiredRoles, result);
|
setCachedPermission(userId, assetId, assetType, requiredRole, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = { hasAccess: false };
|
const result = { hasAccess: false };
|
||||||
setCachedPermission(userId, assetId, assetType, requiredRoles, result);
|
setCachedPermission(userId, assetId, assetType, requiredRole, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,24 +149,18 @@ export function computeEffectivePermission(
|
||||||
* Map workspace sharing level to permission role
|
* Map workspace sharing level to permission role
|
||||||
*/
|
*/
|
||||||
function mapWorkspaceSharingToRole(workspaceSharing: WorkspaceSharing): AssetPermissionRole | null {
|
function mapWorkspaceSharingToRole(workspaceSharing: WorkspaceSharing): AssetPermissionRole | null {
|
||||||
if (workspaceSharing === 'can_view') {
|
switch (workspaceSharing) {
|
||||||
return 'can_view';
|
case 'can_view':
|
||||||
|
return 'can_view';
|
||||||
|
case 'can_edit':
|
||||||
|
return 'can_edit';
|
||||||
|
case 'full_access':
|
||||||
|
return 'full_access';
|
||||||
|
case 'none':
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if (workspaceSharing === 'can_edit') {
|
|
||||||
return 'can_edit';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workspaceSharing === 'full_access') {
|
|
||||||
return 'full_access';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workspaceSharing === 'none') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _exhaustiveCheck: never = workspaceSharing;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -204,7 +204,7 @@ export async function hasAssetPermission(params: {
|
||||||
userId: string;
|
userId: string;
|
||||||
assetId: string;
|
assetId: string;
|
||||||
assetType: AssetType;
|
assetType: AssetType;
|
||||||
requiredRole: AssetPermissionRole | AssetPermissionRole[];
|
requiredRole: AssetPermissionRole;
|
||||||
organizationId?: string;
|
organizationId?: string;
|
||||||
workspaceSharing?: WorkspaceSharing;
|
workspaceSharing?: WorkspaceSharing;
|
||||||
}): Promise<boolean> {
|
}): Promise<boolean> {
|
||||||
|
|
Loading…
Reference in New Issue