66using System . Text . Json ;
77using System . Threading . Tasks ;
88using NuGet . Services . Entities ;
9+ using NuGetGallery . Auditing ;
910using NuGetGallery . Authentication ;
1011using NuGetGallery . Infrastructure . Authentication ;
1112
@@ -50,6 +51,7 @@ public class FederatedCredentialService : IFederatedCredentialService
5051 private readonly IEntraIdTokenValidator _entraIdTokenValidator ;
5152 private readonly ICredentialBuilder _credentialBuilder ;
5253 private readonly IAuthenticationService _authenticationService ;
54+ private readonly IAuditingService _auditingService ;
5355 private readonly IDateTimeProvider _dateTimeProvider ;
5456 private readonly IFeatureFlagService _featureFlagService ;
5557 private readonly IFederatedCredentialConfiguration _configuration ;
@@ -61,6 +63,7 @@ public FederatedCredentialService(
6163 IEntraIdTokenValidator entraIdTokenValidator ,
6264 ICredentialBuilder credentialBuilder ,
6365 IAuthenticationService authenticationService ,
66+ IAuditingService auditingService ,
6467 IDateTimeProvider dateTimeProvider ,
6568 IFeatureFlagService featureFlagService ,
6669 IFederatedCredentialConfiguration configuration )
@@ -71,6 +74,7 @@ public FederatedCredentialService(
7174 _entraIdTokenValidator = entraIdTokenValidator ?? throw new ArgumentNullException ( nameof ( EntraIdTokenValidator ) ) ;
7275 _credentialBuilder = credentialBuilder ?? throw new ArgumentNullException ( nameof ( credentialBuilder ) ) ;
7376 _authenticationService = authenticationService ?? throw new ArgumentNullException ( nameof ( authenticationService ) ) ;
77+ _auditingService = auditingService ?? throw new ArgumentNullException ( nameof ( auditingService ) ) ;
7478 _dateTimeProvider = dateTimeProvider ?? throw new ArgumentNullException ( nameof ( dateTimeProvider ) ) ;
7579 _featureFlagService = featureFlagService ?? throw new ArgumentNullException ( nameof ( featureFlagService ) ) ;
7680 _configuration = configuration ?? throw new ArgumentNullException ( nameof ( configuration ) ) ;
@@ -112,6 +116,8 @@ public async Task<AddFederatedCredentialPolicyResult> AddEntraIdServicePrincipal
112116
113117 await _repository . AddPolicyAsync ( policy , saveChanges : true ) ;
114118
119+ await _auditingService . SaveAuditRecordAsync ( FederatedCredentialPolicyAuditRecord . Create ( policy ) ) ;
120+
115121 return AddFederatedCredentialPolicyResult . Created ( policy ) ;
116122 }
117123
@@ -123,7 +129,13 @@ public async Task DeletePolicyAsync(FederatedCredentialPolicy policy)
123129 await _authenticationService . RemoveCredential ( policy . CreatedBy , credential , commitChanges : false ) ;
124130 }
125131
132+ // Initialize the audit record before deletion so details are still available.
133+ // Entity Framework unlinks navigation properties.
134+ var auditRecord = FederatedCredentialPolicyAuditRecord . Delete ( policy , credentials ) ;
135+
126136 await _repository . DeletePolicyAsync ( policy , saveChanges : true ) ;
137+
138+ await _auditingService . SaveAuditRecordAsync ( auditRecord ) ;
127139 }
128140
129141 public async Task < GenerateApiKeyResult > GenerateApiKeyAsync ( string username , string bearerToken )
@@ -204,9 +216,18 @@ private static GenerateApiKeyResult NoMatchingPolicy(string username)
204216 }
205217 catch ( DataException ex ) when ( ex . IsSqlUniqueConstraintViolation ( ) )
206218 {
219+ await _auditingService . SaveAuditRecordAsync ( FederatedCredentialPolicyAuditRecord . RejectReplay (
220+ evaluation . MatchedPolicy ,
221+ evaluation . FederatedCredential ) ) ;
222+
207223 return GenerateApiKeyResult . Unauthorized ( "This bearer token has already been used. A new bearer token must be used for each request." ) ;
208224 }
209225
226+ await _auditingService . SaveAuditRecordAsync ( FederatedCredentialPolicyAuditRecord . ExchangeForApiKey (
227+ evaluation . MatchedPolicy ,
228+ evaluation . FederatedCredential ,
229+ apiKeyCredential ) ) ;
230+
210231 return null ;
211232 }
212233
0 commit comments