public async Task IncompleteStateIsProcessedAndSavedOnSuccess() { // Arrange ValidatorStatus status = new ValidatorStatus() { State = ValidationStatus.Incomplete }; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); _validatorStateService.Setup(s => s.SaveStatusAsync(It.IsAny <ValidatorStatus>())).ReturnsAsync(SaveStatusResult.Success); _symbolService .Setup(s => s.ValidateSymbolsAsync(It.IsAny <SymbolsValidatorMessage>(), It.IsAny <CancellationToken>())) .ReturnsAsync(NuGetValidationResponse.Succeeded); // Act var result = await Target.HandleAsync(_message); // Assert Assert.True(result); _validatorStateService.Verify(ss => ss.SaveStatusAsync(It.IsAny <ValidatorStatus>()), Times.Once); _validationEnqueuer.Verify( x => x.SendMessageAsync(It.IsAny <PackageValidationMessageData>()), Times.Once); _validationEnqueuer.Verify( x => x.SendMessageAsync(It.Is <PackageValidationMessageData>(d => d.Type == PackageValidationMessageType.CheckValidator)), Times.Once); }
public async Task <ValidatorStatus> TryUpdateValidationStatusAsync( INuGetValidationRequest request, ValidatorStatus validatorStatus, ValidationStatus desiredState) { validatorStatus.State = desiredState; var result = await SaveStatusAsync(validatorStatus); if (result == SaveStatusResult.StaleStatus) { // The save operation fails if another instance of this service has already modified the status. // This may happen due to repeated operations kicked off by the Orchestrator. Return the result // from the other update. _logger.LogWarning( Error.ValidatorStateServiceFailedToUpdateStatus, "Failed to save validation status for {ValidationId} ({PackageId} {PackageVersion}) as the current status is stale", request.ValidationId, request.PackageId, request.PackageVersion); return(await GetStatusAsync(request)); } else if (result != SaveStatusResult.Success) { throw new NotSupportedException($"Unknown {nameof(SaveStatusResult)}: {result}"); } return(validatorStatus); }
public async Task WhenRepositorySigningIsDisabled_SuppressesNupkgUrl() { _config.RepositorySigningEnabled = false; var request = new ValidationRequest(Guid.NewGuid(), 42, "somepackage", "somversion", "https://example.com/package.nupkg"); var status = new ValidatorStatus { State = ValidationStatus.Incomplete, NupkgUrl = "https://nuget.test/package.nupkg", ValidatorIssues = new List <ValidatorIssue>() }; _validatorStateServiceMock .Setup(vss => vss.GetStatusAsync(request)) .ReturnsAsync(status); var result = await _target.GetResultAsync(request); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(request), Times.Once); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(It.IsAny <ValidationRequest>()), Times.Once); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(It.IsAny <Guid>()), Times.Never); Assert.Empty(result.Issues); Assert.Equal(status.State, result.Status); Assert.Null(result.NupkgUrl); }
public async Task ForwardsCallToValidatorStateService() { var request = new ValidationRequest(Guid.NewGuid(), 42, "somepackage", "somversion", "https://example.com/package.nupkg"); var status = new ValidatorStatus { State = ValidationStatus.Incomplete, NupkgUrl = null, ValidatorIssues = new List <ValidatorIssue>() }; _validatorStateServiceMock .Setup(vss => vss.GetStatusAsync(request)) .ReturnsAsync(status); var result = await _target.GetResultAsync(request); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(request), Times.Once); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(It.IsAny <ValidationRequest>()), Times.Once); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(It.IsAny <Guid>()), Times.Never); Assert.Empty(result.Issues); Assert.Equal(status.State, result.Status); Assert.Equal(status.NupkgUrl, result.NupkgUrl); }
/// <summary> /// Maps the provided validation status entity and its associated issues to a <see cref="INuGetValidationResponse"/>. /// This method does not attempt to deserialize the issue data. /// </summary> public static INuGetValidationResponse ToNuGetValidationResponse(this ValidatorStatus validatorStatus) { if (validatorStatus == null) { throw new ArgumentNullException(nameof(validatorStatus)); } if (validatorStatus.ValidatorIssues == null) { throw new ArgumentException( $"The {nameof(ValidatorStatus.ValidatorIssues)} property must not be null.", nameof(validatorStatus)); } /// Don't attempt to deserialize the issues. Instead, pass the issue code and data along to the orchestrator /// to be persisted as-is. This makes the orchestrator more resilient when having outdated version of the /// issues library. Note that this essentially assumes that the data stored in <see cref="ValidatorIssue"/> /// and <see cref="PackageValidationIssue"/> have the same schema. var issues = validatorStatus .ValidatorIssues .Select(x => new SerializedValidationIssue(x.IssueCode, x.Data)) .ToList(); return(new NuGetValidationResponse(validatorStatus.State, issues, validatorStatus.NupkgUrl)); }
public async Task DoesNotSkipCheckWhenPackageFitsCriteria() { var request = new ValidationRequest(Guid.NewGuid(), 42, "somepackage", "somversion", "https://example.com/package.nupkg"); var status = new ValidatorStatus { State = ValidationStatus.NotStarted, NupkgUrl = null, ValidatorIssues = new List <ValidatorIssue>() }; _criteriaEvaluatorMock .Setup(ce => ce.IsMatch(It.IsAny <ICriteria>(), It.IsAny <SymbolPackage>())) .Returns(false); _validatorStateServiceMock .Setup(vss => vss.GetStatusAsync(request)) .ReturnsAsync(status); var result = await _target.GetResultAsync(request); Assert.Equal(ValidationStatus.NotStarted, result.Status); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(It.IsAny <ValidationRequest>()), Times.Once); _validatorStateServiceMock .Verify(vss => vss.GetStatusAsync(It.IsAny <Guid>()), Times.Never); }
private async Task <bool> SaveStatusAsync(ValidatorStatus validation, SignatureValidationMessage message) { try { var saveStatus = await _validatorStateService.SaveStatusAsync(validation); if (saveStatus == SaveStatusResult.Success) { // Consume the message. return(true); } else { _logger.LogWarning( "Unable to save to save due to stale context, requeueing package {PackageId} {PackageVersion} for validation id: {ValidationId}.", message.PackageId, message.PackageVersion, message.ValidationId); } } catch (DbUpdateException e) when(e.IsUniqueConstraintViolationException()) { _logger.LogWarning( 0, e, "Unable to save to save due to unique contrainst violation, requeueing package {PackageId} {PackageVersion} for validation id: {ValidationId}.", message.PackageId, message.PackageVersion, message.ValidationId); } // Message may be retried. return(false); }
public async Task DoesNotEnqueueIfFeatureFlagIsOff() { // Arrange ValidatorStatus status = new ValidatorStatus() { State = ValidationStatus.Incomplete }; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); _validatorStateService.Setup(s => s.SaveStatusAsync(It.IsAny <ValidatorStatus>())).ReturnsAsync(SaveStatusResult.Success); _featureFlagService.Setup(x => x.IsQueueBackEnabled()).Returns(false); _symbolService .Setup(s => s.ValidateSymbolsAsync(It.IsAny <SymbolsValidatorMessage>(), It.IsAny <CancellationToken>())) .ReturnsAsync(NuGetValidationResponse.Succeeded); // Act var result = await Target.HandleAsync(_message); // Assert Assert.True(result); _validatorStateService.Verify(ss => ss.SaveStatusAsync(It.IsAny <ValidatorStatus>()), Times.Once); _validationEnqueuer.Verify( x => x.SendMessageAsync(It.IsAny <PackageValidationMessageData>()), Times.Never); }
public ValidateAsync() { _packageMock = new Mock <ISignedPackageReader>(); _packageMock .Setup(x => x.IsSignedAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(false); _validation = new ValidatorStatus { PackageKey = 42, State = ValidationStatus.NotStarted, }; _message = new SignatureValidationMessage( "NuGet.Versioning", "4.3.0", new Uri("https://example/nuget.versioning.4.3.0.nupkg"), new Guid("b777135f-1aac-4ec2-a3eb-1f64fe1880d5")); _cancellationToken = CancellationToken.None; _packageSigningStateService = new Mock <IPackageSigningStateService>(); _certificates = new Mock <IEntityRepository <Certificate> >(); _logger = new Mock <ILogger <SignatureValidator> >(); _certificates .Setup(x => x.GetAll()) .Returns(Enumerable.Empty <Certificate>().AsQueryable()); _target = new SignatureValidator( _packageSigningStateService.Object, _certificates.Object, _logger.Object); }
private async Task HandleSignedPackageAsync( ISignedPackageReader signedPackageReader, ValidatorStatus validation, SignatureValidationMessage message, CancellationToken cancellationToken) { // Block packages that don't have exactly one signature. var packageSignatures = await signedPackageReader.GetSignaturesAsync(cancellationToken); if (packageSignatures.Count != 1) { _logger.LogInformation( "Signed package {PackageId} {PackageVersion} is blocked for validation {ValidationId} since it has {SignatureCount} signatures.", message.PackageId, message.PackageVersion, message.ValidationId, packageSignatures.Count); await RejectAsync(validation, message); return; } // Block packages with any unknown signing certificates. var packageThumbprints = GetThumbprints(packageSignatures); var knownThumbprints = _certificates .GetAll() .Where(c => packageThumbprints.Contains(c.Thumbprint)) .Select(c => c.Thumbprint) .ToList(); var unknownThumbprints = packageThumbprints.Except(knownThumbprints); if (unknownThumbprints.Any()) { _logger.LogInformation( "Signed package {PackageId} {PackageVersion} is blocked for validation {ValidationId} since it has unknown certificate thumbprints: {UnknownThumbprints}", message.PackageId, message.PackageVersion, message.ValidationId, unknownThumbprints); await RejectAsync(validation, message); return; } // For now, we assume safe-listed certificates are totally valid. _logger.LogInformation( "Signed package {PackageId} {PackageVersion} is accepted for validation {ValidationId} with certificate thumbprints: {PackageThumbprints}", message.PackageId, message.PackageVersion, message.ValidationId, packageThumbprints); await AcceptAsync(validation, message, PackageSigningStatus.Valid); return; }
public void RejectsNullValidatorStatus() { // Arrange ValidatorStatus validatorStatus = null; // Act & Assert var ex = Assert.Throws <ArgumentNullException>(() => validatorStatus.ToNuGetValidationResponse()); Assert.Equal("validatorStatus", ex.ParamName); }
private async Task RejectAsync(ValidatorStatus validation, SignatureValidationMessage message) { await _packageSigningStateService.SetPackageSigningState( validation.PackageKey, message.PackageId, message.PackageVersion, status : PackageSigningStatus.Invalid); validation.State = ValidationStatus.Failed; }
private async Task AcceptAsync(ValidatorStatus validation, SignatureValidationMessage message, PackageSigningStatus status) { await _packageSigningStateService.SetPackageSigningState( validation.PackageKey, message.PackageId, message.PackageVersion, status); validation.State = ValidationStatus.Succeeded; }
public async Task ValidateAsync(ISignedPackageReader signedPackageReader, ValidatorStatus validation, SignatureValidationMessage message, CancellationToken cancellationToken) { if (!await signedPackageReader.IsSignedAsync(cancellationToken)) { await HandleUnsignedPackageAsync(validation, message); } else { await HandleSignedPackageAsync(signedPackageReader, validation, message, cancellationToken); } }
private async Task HandleUnsignedPackageAsync(ValidatorStatus validation, SignatureValidationMessage message) { _logger.LogInformation( "Package {PackageId} {PackageVersion} is unsigned, no additional validations necessary for {ValidationId}.", message.PackageId, message.PackageVersion, message.ValidationId); await AcceptAsync(validation, message, PackageSigningStatus.Unsigned); return; }
public TheCleanUpAsyncMethod(ITestOutputHelper output) : base(output) { _validatorStatus = new ValidatorStatus(); _validatorStateService .Setup(x => x.GetStatusAsync(It.IsAny <IValidationRequest>())) .ReturnsAsync(() => _validatorStatus); _blob = new Mock <ISimpleCloudBlob>(MockBehavior.Strict); _blobProvider .Setup(x => x.GetBlobFromUrl(It.IsAny <string>())) .Returns(() => _blob.Object); }
public async Task ReturnsFalseWhenTheValidatorStateIsNotSaved() { // Arrange ValidatorStatus status = null; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); // Act var result = await Target.HandleAsync(_message); // Assert Assert.False(result); }
public void RejectsNullValidatorIssues() { // Arrange var validatorStatus = new ValidatorStatus { ValidatorIssues = null, }; // Act & Assert var ex = Assert.Throws <ArgumentException>(() => validatorStatus.ToNuGetValidationResponse()); Assert.Contains("The ValidatorIssues property must not be null.", ex.Message); Assert.Equal("validatorStatus", ex.ParamName); }
public async Task ReturnsFalseWhenTheValidatorStateIsNotSaved() { // Arrange ValidatorStatus status = null; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); var handler = new SymbolsValidatorMessageHandler(_logger.Object, _symbolService.Object, _validatorStateService.Object); // Act var result = await handler.HandleAsync(_message); // Assert Assert.False(result); }
public HandleAsync() { _message = new SignatureValidationMessage( "NuGet.Versioning", "4.3.0", TestPackageUri, new Guid("18e83aca-953a-4484-a698-a8fb8619e0bd")); _outputNupkgUri = new Uri("https://example/processor/18e83aca-953a-4484-a698-a8fb8619e0bd/nuget.versioning.4.3.0.nupkg"); _validation = new ValidatorStatus { PackageKey = 42, State = ValidationStatus.Incomplete, }; _validationIssue = new Mock <IValidationIssue>(); _validatorResult = new SignatureValidatorResult(ValidationStatus.Succeeded, nupkgUri: null); _packageDownloader = new Mock <IFileDownloader>(); _validatorStateService = new Mock <IValidatorStateService>(); _signatureValidator = new Mock <ISignatureValidator>(); _validationEnqueuer = new Mock <IPackageValidationEnqueuer>(); _featureFlagService = new Mock <IFeatureFlagService>(); _logger = new Mock <ILogger <SignatureValidationMessageHandler> >(); _packageDownloader .Setup(x => x.DownloadAsync(_message.NupkgUri, It.IsAny <CancellationToken>())) .ReturnsAsync(() => FileDownloadResult.Ok(TestResources.GetResourceStream(TestResources.UnsignedPackage))); _validatorStateService .Setup(x => x.GetStatusAsync(It.IsAny <Guid>())) .ReturnsAsync(() => _validation); _signatureValidator .Setup(x => x.ValidateAsync( It.IsAny <int>(), It.IsAny <Stream>(), It.IsAny <SignatureValidationMessage>(), It.IsAny <CancellationToken>())) .ReturnsAsync(() => _validatorResult); _featureFlagService.SetReturnsDefault(true); _target = new SignatureValidationMessageHandler( _packageDownloader.Object, _validatorStateService.Object, _signatureValidator.Object, _validationEnqueuer.Object, _featureFlagService.Object, _logger.Object); }
public TheStartAsyncMethod() { _request = new ValidationRequest(Guid.NewGuid(), 42, "somepackage", "somversion", "https://example.com/package.nupkg"); _status = new ValidatorStatus { State = ValidationStatus.NotStarted, NupkgUrl = null, ValidatorIssues = new List <ValidatorIssue>() }; _validatorStateServiceMock .Setup(vss => vss.GetStatusAsync(_request)) .ReturnsAsync(_status); _validatorStateServiceMock .Setup(vss => vss.TryAddValidatorStatusAsync(It.IsAny <IValidationRequest>(), It.IsAny <ValidatorStatus>(), It.IsAny <ValidationStatus>())) .ReturnsAsync(_status); }
public void AllowsEmptyIssueList() { // Arrange var validatorStatus = new ValidatorStatus { State = ValidationStatus.Succeeded, ValidatorIssues = new List <ValidatorIssue>(), }; // Act var result = validatorStatus.ToNuGetValidationResponse(); // Assert Assert.Equal(ValidationStatus.Succeeded, result.Status); Assert.NotNull(result.Issues); Assert.Empty(result.Issues); }
public async Task ReturnsTrueWhenTheValidatorStateIsSucceded() { // Arrange ValidatorStatus status = new ValidatorStatus() { State = ValidationStatus.Succeeded }; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); var handler = new SymbolsValidatorMessageHandler(_logger.Object, _symbolService.Object, _validatorStateService.Object); // Act var result = await handler.HandleAsync(_message); // Assert Assert.True(result); }
public async Task IncompleteStateIsProcessed() { // Arrange ValidatorStatus status = new ValidatorStatus() { State = ValidationStatus.Incomplete }; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); _symbolService.Setup(s => s.ValidateSymbolsAsync(It.IsAny <SymbolsValidatorMessage>(), It.IsAny <CancellationToken>())).ReturnsAsync(ValidationResult.Incomplete); // Act var result = await Target.HandleAsync(_message); // Assert Assert.False(result); _symbolService.Verify(ss => ss.ValidateSymbolsAsync(It.IsAny <SymbolsValidatorMessage>(), It.IsAny <CancellationToken>()), Times.Once); }
public HandleAsync() { _message = new SignatureValidationMessage( "NuGet.Versioning", "4.3.0", TestPackageUri, new Guid("18e83aca-953a-4484-a698-a8fb8619e0bd")); _validation = new ValidatorStatus { PackageKey = 42, State = ValidationStatus.Incomplete, }; _urlToResourceName = new Dictionary <Uri, string> { { _message.NupkgUri, TestResources.UnsignedPackage }, }; _handler = new EmbeddedResourceTestHandler(_urlToResourceName); _httpClient = new HttpClient(_handler); _validatorStateService = new Mock <IValidatorStateService>(); _signatureValidator = new Mock <ISignatureValidator>(); _logger = new Mock <ILogger <SignatureValidationMessageHandler> >(); _validatorStateService .Setup(x => x.GetStatusAsync(It.IsAny <Guid>())) .ReturnsAsync(() => _validation); _signatureValidator .Setup(x => x.ValidateAsync( It.IsAny <ISignedPackageReader>(), It.IsAny <ValidatorStatus>(), It.IsAny <SignatureValidationMessage>(), It.IsAny <CancellationToken>())) .Returns(Task.CompletedTask) .Callback(() => _validation.State = ValidationStatus.Succeeded); _target = new SignatureValidationMessageHandler( _httpClient, _validatorStateService.Object, _signatureValidator.Object, _logger.Object); }
public async Task DeletesNothingWhenThereIsNoNupkgUrl() { var request = new ValidationRequest(Guid.NewGuid(), 42, "somepackage", "somversion", "https://nuget.test/package.nupkg"); var status = new ValidatorStatus { State = ValidationStatus.Incomplete, NupkgUrl = null, ValidatorIssues = new List <ValidatorIssue>() }; _validatorStateServiceMock .Setup(v => v.GetStatusAsync(request)) .ReturnsAsync(status); await _target.CleanUpAsync(request); _validatorStateServiceMock.Verify(x => x.GetStatusAsync(request), Times.Once); _blobProvider.Verify(x => x.GetBlobFromUrl(It.IsAny <string>()), Times.Never); }
public async Task ReturnsTrueWhenTheValidatorStateIsFailed() { // Arrange ValidatorStatus status = new ValidatorStatus() { State = ValidationStatus.Failed }; _validatorStateService.Setup(s => s.GetStatusAsync(It.IsAny <Guid>())).ReturnsAsync(status); // Act var result = await Target.HandleAsync(_message); // Assert Assert.True(result); _validationEnqueuer.Verify( x => x.SendMessageAsync(It.IsAny <PackageValidationMessageData>()), Times.Never); }
private async Task <bool> SaveStatusAsync(ValidatorStatus validation, SymbolsValidatorMessage message, int maxRetries) { bool saveStatus = false; int currentRetry = 0; while (!saveStatus && ++currentRetry < maxRetries) { try { _logger.LogWarning( "{ValidatorName}:Try to save validation status package {PackageId} {PackageVersion} for validation id: {ValidationId} RetryCount: {currentRetry}.", ValidatorName.SymbolsValidator, message.PackageId, message.PackageNormalizedVersion, message.ValidationId, currentRetry); saveStatus = await _validatorStateService.SaveStatusAsync(validation) == SaveStatusResult.Success; } catch (Exception e) { _logger.LogWarning( 0, e, "{ValidatorName}:Unable to save to save package {PackageId} {PackageVersion} for validation id: {ValidationId}.", ValidatorName.SymbolsValidator, message.PackageId, message.PackageNormalizedVersion, message.ValidationId); } } if (!saveStatus) { _logger.LogWarning( "{ValidatorName}:Unable to save to save after {MaxRetries}. Requeueing package {PackageId} {PackageVersion} for validation id: {ValidationId}.", ValidatorName.SymbolsValidator, maxRetries, message.PackageId, message.PackageNormalizedVersion, message.ValidationId); } return(saveStatus); }
public async Task WhenThereIsNupkgUrl_DeletesTheBlobIfRepositorySigningIsEnabled(bool repositorySigningEnabled, bool expectsBlobDeleted) { // Arrange _config.RepositorySigningEnabled = repositorySigningEnabled; var request = new ValidationRequest(Guid.NewGuid(), 42, "somepackage", "somversion", "https://nuget.test/package.nupkg"); var nupkgUrl = "http://example/packages/nuget.versioning.4.6.0.nupkg"; var status = new ValidatorStatus { State = ValidationStatus.Incomplete, NupkgUrl = nupkgUrl, ValidatorIssues = new List <ValidatorIssue>() }; _validatorStateServiceMock .Setup(v => v.GetStatusAsync(request)) .ReturnsAsync(status); var blob = new Mock <ISimpleCloudBlob>(); _blobProvider .Setup(x => x.GetBlobFromUrl(nupkgUrl)) .Returns(blob.Object); // Act await _target.CleanUpAsync(request); // Assert _validatorStateServiceMock.Verify(x => x.GetStatusAsync(request), Times.Once); if (expectsBlobDeleted) { _blobProvider.Verify(x => x.GetBlobFromUrl(nupkgUrl), Times.Once); blob.Verify(x => x.DeleteIfExistsAsync(), Times.Once); } else { _blobProvider.Verify(x => x.GetBlobFromUrl(nupkgUrl), Times.Never); blob.Verify(x => x.DeleteIfExistsAsync(), Times.Never); } }
public async Task <SaveStatusResult> SaveStatusAsync(ValidatorStatus status) { if (status.ValidatorName != _validatorName) { throw new ArgumentException( $"Expected validator name '{_validatorName}', actual: '{status.ValidatorName}'", nameof(status)); } try { await _validationContext.SaveChangesAsync(); return(SaveStatusResult.Success); } catch (DbUpdateConcurrencyException) { return(SaveStatusResult.StaleStatus); } }
public void Validate() { if (status != ValidatorStatus.Created) { throw new WorkflowException( $"Validator {EntityName} has already been executed. You cannot use validators more than one time."); } status = ValidatorStatus.InProgress; ValidateLogic(); foreach (SelpValidator nestedValidator in NestedValidators) { nestedValidator.Validate(); } status = ValidatorStatus.Validated; }
protected SelpValidator() { NestedValidators = new List<SelpValidator>(); status = ValidatorStatus.Created; errors = new List<ValidatorError>(); }