Skip to main content

Assets

Introduction

The Assets module manages network assets that belong to organizations. Assets represent protected infrastructure (servers, networks, services) that are monitored by sensors. Each asset can be linked to an organization, a sensor, and a person-in-charge (PIC) user. Business logic is encapsulated in AssetService with data access via AssetRepository.


Controller

File: API/Controllers/Assets/AssetsController.cs Route: api/assets Inherits: ControllerBase Auth: [Authorize] (all endpoints require authentication) Delegates to: IAssetService

Endpoints

EndpointMethodDescription
api/assetsGETList all assets in current org
api/assets/{id}GETGet single asset by ID
api/assetsPOSTCreate new asset
api/assets/{id}PUTUpdate existing asset
api/assets/{id}DELETEDelete asset

Organization Scoping

The list endpoint extracts the organization ID from JWT claims:

var organizationId = UserHelper.GetOrganizationId(User);
if (!organizationId.HasValue)
{
return BadRequest(ApiEnvelope.Error("Organization ID not found in token", HttpContext.TraceIdentifier));
}
var assets = await _assetService.GetAssetsAsync(organizationId.Value);

Response Format

All endpoints use the standard ApiEnvelope response:

{
"success": true,
"data": {
"items": [...],
"count": 5
},
"errors": []
}

Entity

File: Domain/Entities/Asset.cs Table: assets Namespace: Ravenxcope.Backend.Domain.Entities

ColumnTypeConstraintsDescription
iduuidPKPrimary key (UUIDv7)
namevarchar(255)RequiredAsset display name
descriptiontextNullableAsset description
locationvarchar(255)NullablePhysical location
as_numbervarchar(255)NullableAutonomous System number
dnsvarchar(255)NullableDNS name or IP
organization_iduuidFK (nullable)Owning organization
sensor_iduuidFK (nullable)Associated sensor
pic_iduuidFK (nullable)Person in charge (User)
created_attimestampCreation timestamp
updated_attimestampLast update timestamp
[ForeignKey("OrganizationId")]
public Organization? Organization { get; set; }

[ForeignKey("SensorId")]
public Sensor? Sensor { get; set; }

[ForeignKey("PicId")]
public User? Pic { get; set; }

Foreign Key Behavior

RelationshipBehavior
Organization → AssetSetNull
Sensor → AssetSetNull
User (PIC) → AssetSetNull

All FKs use SetNull on delete — deleting the parent entity sets the FK to null rather than cascading the delete.


Service

File: Infrastructure/Services/AssetService.cs Interface: IAssetService Lifetime: Scoped

Dependencies

DependencyPurpose
IAssetRepositoryAsset CRUD operations
IOrganizationRepositoryValidate organization exists on create
ISensorRepositoryValidate sensor exists on create/update
IUserRepositoryValidate PIC user exists on create/update

Service Methods

MethodReturnsDescription
GetAssetsAsync(Guid orgId)IReadOnlyList<AssetDto>List assets by organization
GetAssetAsync(Guid id)AssetDtoGet single asset with relations
CreateAssetAsync(CreateAssetRequest)AssetDtoCreate asset with FK validation
UpdateAssetAsync(Guid, UpdateAssetRequest)AssetDtoUpdate asset fields with FK validation
DeleteAssetAsync(Guid id)voidHard delete asset

Validation

The ValidateReferencesAsync method validates FK references before create/update:

  1. If organizationId is provided → verify organization exists
  2. If sensorId is provided → verify sensor exists
  3. If picId is provided → verify user exists

Throws ArgumentException with descriptive message on validation failure.


Repository

File: Infrastructure/Repositories/AssetRepository.cs Interface: IAssetRepository Base: EfRepository<Asset> Lifetime: Scoped

Custom Query Methods

MethodReturnsDescription
GetByOrganizationIdAsync(Guid)IReadOnlyList<Asset>Assets with Organization, Sensor, PIC included
GetByIdWithRelationsAsync(Guid)Asset?Single asset with all navigation properties loaded

DTOs

Request DTOs

File: Application/DTOs/CreateAssetRequest.cs

PropertyTypeRequiredDescription
NamestringYesAsset name
Descriptionstring?NoAsset description
Locationstring?NoPhysical location
AsNumberstring?NoAS number
Dnsstring?NoDNS name or IP
OrganizationIdGuid?NoOwning organization
SensorIdGuid?NoAssociated sensor
PicIdGuid?NoPerson in charge

File: Application/DTOs/UpdateAssetRequest.cs

Same fields as CreateAssetRequest except OrganizationId (only Name, Description, Location, AsNumber, Dns, SensorId, PicId). All fields are nullable for partial updates.

Response DTO

File: Application/DTOs/AssetDtos.cs

public sealed record AssetDto(
Guid Id,
string Name,
string? Description,
string? Location,
string? AsNumber,
string? Dns,
Guid? OrganizationId,
string? OrganizationName, // Resolved from Organization navigation
Guid? SensorId,
string? SensorName, // Resolved from Sensor navigation
Guid? PicId,
string? PicName, // Resolved from User (PIC) navigation
DateTime CreatedAt,
DateTime UpdatedAt);

Seeded Permissions

Asset CRUD permissions are seeded in ApplicationDbContext.OnModelCreating:

PermissionSlug
Create Assetcreate-asset
Edit Assetedit-asset
Delete Assetdelete-asset
Read Assetread-asset

DI Registration

In ServiceCollectionExtensions.AddBackendServicesAsync:

services.AddScoped<IAssetRepository, AssetRepository>();
services.AddScoped<IAssetService, AssetService>();

Future Improvements

Completed

  • Permission-based authorization on write endpoints (manage-assets policy).

Open

  • Add pagination to asset listing endpoint.
  • Add bulk import/export for assets.
  • Add asset status tracking (active, decommissioned, etc.).