diff --git a/backend/app/services/asset/asset.py b/backend/app/services/asset/asset.py index ef9ddb5..6bb6859 100644 --- a/backend/app/services/asset/asset.py +++ b/backend/app/services/asset/asset.py @@ -25,7 +25,7 @@ def get_assets_service( """ base_statement = select(Asset).where(Asset.assessment_id == assessment_id) - if user.assessment_acl_role != AclRole.RED: + if user.assessment_acl_role == AclRole.SPECTATOR: base_statement = base_statement.filter(Asset.deleted.is_(False)) return paginated_query(session, Asset, filter_query, base_statement=base_statement) diff --git a/backend/tests/services/asset/test_asset.py b/backend/tests/services/asset/test_asset.py index 258ca09..7830306 100644 --- a/backend/tests/services/asset/test_asset.py +++ b/backend/tests/services/asset/test_asset.py @@ -3,7 +3,7 @@ import pytest from fastapi import HTTPException -from app.enums.enums import ActivityAssetRole +from app.enums.enums import AclRole, ActivityAssetRole from app.models.activity import Activity from app.models.assessment import Assessment from app.schemas.asset import AssetBase, AssetFilter @@ -60,6 +60,44 @@ def test_get_assets(session, test_assessment, test_admin_user): assert assets.total == 2 +def test_get_assets_deleted_visibility_by_role( + session, test_assessment, test_admin_user +): + create_asset_service( + AssetBase(name="Active", icon="server", properties={}), + test_assessment.id, + test_admin_user, + session, + ) + deleted = create_asset_service( + AssetBase(name="Deleted", icon="server", properties={}), + test_assessment.id, + test_admin_user, + session, + ) + toggle_asset_delete_service( + deleted.id, test_assessment.id, test_admin_user, session + ) + + test_admin_user.assessment_acl_role = AclRole.BLUE + blue_view = get_assets_service( + test_assessment.id, test_admin_user, session, AssetFilter() + ) + assert blue_view.total == 2 + + test_admin_user.assessment_acl_role = AclRole.RED + red_view = get_assets_service( + test_assessment.id, test_admin_user, session, AssetFilter() + ) + assert red_view.total == 2 + + test_admin_user.assessment_acl_role = AclRole.SPECTATOR + spectator_view = get_assets_service( + test_assessment.id, test_admin_user, session, AssetFilter() + ) + assert spectator_view.total == 1 + + def test_get_assets_with_filter(session, test_assessment, test_admin_user): asset1 = AssetBase(name="WebServer", icon="server", properties={}) asset2 = AssetBase(name="Database", icon="database", properties={}) diff --git a/frontend/src/components/assessment/ActivityAssetsManager.vue b/frontend/src/components/assessment/ActivityAssetsManager.vue index 67228e4..c30974e 100644 --- a/frontend/src/components/assessment/ActivityAssetsManager.vue +++ b/frontend/src/components/assessment/ActivityAssetsManager.vue @@ -49,6 +49,7 @@ const props = defineProps<{ compact?: boolean; readonly?: boolean; availableAssets?: AssetRead[]; + accentColor?: 'green' | 'orange' | 'purple' | 'blue'; }>(); const emit = defineEmits<{ @@ -97,6 +98,27 @@ function getIconComponent(iconName: string | null | undefined) { return (iconName && iconMap[iconName]) || Computer; } +const ACCENT_LABEL_CLASS: Record = { + green: 'text-green-600 dark:text-green-400', + orange: 'text-orange-600 dark:text-orange-400', + purple: 'text-purple-600 dark:text-purple-400', + blue: 'text-blue-600 dark:text-blue-400', +}; +const ACCENT_BORDER_CLASS: Record = { + green: 'border-l-green-500', + orange: 'border-l-orange-500', + purple: 'border-l-purple-500', + blue: 'border-l-blue-700', +}; +const DEFAULT_SECTION_ACCENT: Record = { + sources: 'green', + targets: 'orange', + tools: 'purple', +}; +function accentFor(key: string): string { + return props.accentColor ?? DEFAULT_SECTION_ACCENT[key] ?? 'green'; +} + // Global asset fetch async function fetchAssets() { loading.value = true; @@ -266,9 +288,7 @@ onMounted(() => {