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.).