public async Task UpdatesTheStreamMetadata() { // Arrange var package = new Package { Hash = "hash-before", HashAlgorithm = "hash-algorithm-before", PackageFileSize = 23, LastUpdated = new DateTime(2017, 1, 1, 8, 30, 0), LastEdited = new DateTime(2017, 1, 1, 7, 30, 0), PackageStatusKey = PackageStatus.Available, }; var metadata = new PackageStreamMetadata { Hash = "hash-after", HashAlgorithm = "hash-algorithm-after", Size = 42, }; var service = CreateService(); // Act var before = DateTime.UtcNow; await service.UpdatePackageStreamMetadataAsync(package, metadata, commitChanges : true); var after = DateTime.UtcNow; // Assert Assert.Equal("hash-after", package.Hash); Assert.Equal("hash-algorithm-after", package.HashAlgorithm); Assert.Equal(42, package.PackageFileSize); Assert.InRange(package.LastUpdated, before, after); Assert.NotNull(package.LastEdited); Assert.InRange(package.LastEdited.Value, before, after); Assert.Equal(package.LastUpdated, package.LastEdited); }
public async Task SetsPackageStreamMetadataIfChanged() { var content = "Hello, world."; var expectedHash = "rQw3wx1psxXzqB8TyM3nAQlK2RcluhsNwxmcqXE2YbgoDW735o8TPmIR4uWpoxUERddvFwjgRSGw7gNPCwuvJg=="; var stream = new MemoryStream(Encoding.ASCII.GetBytes(content)); PackageStreamMetadata actual = null; PackageFileServiceMock .Setup(x => x.DownloadPackageFileToDiskAsync(ValidationSet)) .ReturnsAsync(stream); var streamMetadata = new PackageStreamMetadata() { Size = stream.Length, Hash = expectedHash, HashAlgorithm = CoreConstants.Sha512HashAlgorithmId }; PackageFileServiceMock .Setup(x => x.UpdatePackageBlobMetadataAsync(It.IsAny <PackageValidationSet>())) .ReturnsAsync(streamMetadata); PackageServiceMock .Setup(x => x.UpdateMetadataAsync(Package, It.IsAny <PackageStreamMetadata>(), false)) .Returns(Task.CompletedTask) .Callback <Package, PackageStreamMetadata, bool>((_, m, __) => actual = m); await Target.SetStatusAsync(PackageValidatingEntity, ValidationSet, PackageStatus.Available); Assert.NotNull(actual); Assert.Equal(content.Length, actual.Size); Assert.Equal(expectedHash, actual.Hash); Assert.Equal("SHA512", actual.HashAlgorithm); PackageServiceMock.Verify( x => x.UpdateMetadataAsync(Package, actual, false), Times.Once); }
public async Task DoesNotSetPackageStreamMetadataIfNotChanged() { var content = "Hello, world."; Package.PackageFileSize = content.Length; Package.HashAlgorithm = "SHA512"; Package.Hash = "rQw3wx1psxXzqB8TyM3nAQlK2RcluhsNwxmcqXE2YbgoDW735o8TPmIR4uWpoxUERddvFwjgRSGw7gNPCwuvJg=="; var stream = new MemoryStream(Encoding.ASCII.GetBytes(content)); var corePackageService = new Mock <ICorePackageService>(); var mockPackageEntityRepository = new Mock <IEntityRepository <Package> >(); var entityPackageService = new PackageEntityService(corePackageService.Object, mockPackageEntityRepository.Object); PackageFileServiceMock .Setup(x => x.DownloadPackageFileToDiskAsync(ValidationSet)) .ReturnsAsync(stream); var streamMetadata = new PackageStreamMetadata() { Size = Package.PackageFileSize, Hash = Package.Hash, HashAlgorithm = Package.HashAlgorithm }; PackageFileServiceMock .Setup(x => x.UpdatePackageBlobMetadataAsync(It.IsAny <PackageValidationSet>())) .ReturnsAsync(streamMetadata); await Target.SetStatusAsync(PackageValidatingEntity, ValidationSet, PackageStatus.Available); corePackageService.Verify( x => x.UpdatePackageStreamMetadataAsync(It.IsAny <Package>(), It.IsAny <PackageStreamMetadata>(), It.IsAny <bool>()), Times.Never); }
public void MetadataChangeInvokeICoreService() { // Arrange var package = CreatePackage(); package.PackageFileSize = 100; package.HashAlgorithm = "SHA512"; package.Hash = "rQw3wx1psxXzqB8TyM3nAQlK2RcluhsNwxmcqXE2YbgoDW735o8TPmIR4uWpoxUERddvFwjgRSGw7gNPCwuvJg=="; var metadata = new PackageStreamMetadata() { Hash = package.Hash, HashAlgorithm = package.HashAlgorithm, Size = package.PackageFileSize * 2 }; var mockCorePackageService = new Mock <ICorePackageService>(); var mockIPackageEntityRepository = new Mock <IEntityRepository <Package> >(); mockCorePackageService.Setup(c => c.UpdatePackageStreamMetadataAsync(package, metadata, true)).Returns(Task.CompletedTask); var service = new PackageEntityService(mockCorePackageService.Object, mockIPackageEntityRepository.Object); // Act & Assert var result = service.UpdateMetadataAsync(package, metadata, true); mockCorePackageService .Verify(s => s.UpdatePackageStreamMetadataAsync(package, metadata, true), Times.Once); }
public void WillCreateAndReturnSymbolPackageButNotCommit() { // Arrange var symbolPackageStreamMetadata = new PackageStreamMetadata() { Size = 12312, Hash = "01asdf2130", HashAlgorithm = "VerySecureAlogrithm" }; var package = new Package(); var mockSymbolPackageRepository = new Mock <IEntityRepository <SymbolPackage> >(); mockSymbolPackageRepository .Setup(x => x.InsertOnCommit(It.IsAny <SymbolPackage>())); var service = CreateService(mockSymbolPackageRepository); // Act var symbolPackage = service.CreateSymbolPackage(package, symbolPackageStreamMetadata); // Assert mockSymbolPackageRepository.Verify(x => x.InsertOnCommit(It.IsAny <SymbolPackage>()), Times.Once); mockSymbolPackageRepository.Verify(x => x.CommitChangesAsync(), Times.Never); Assert.NotNull(symbolPackage); Assert.Equal(symbolPackageStreamMetadata.Hash, symbolPackage.Hash); Assert.Equal(symbolPackageStreamMetadata.HashAlgorithm, symbolPackage.HashAlgorithm); Assert.Equal(symbolPackageStreamMetadata.Size, symbolPackage.FileSize); Assert.Equal(package, symbolPackage.Package); }
public async Task <Package> ReflowAsync(string id, string version) { var package = _packageService.FindPackageByIdAndVersionStrict(id, version); if (package == null) { return(null); } EntitiesConfiguration.SuspendExecutionStrategy = true; using (var transaction = _entitiesContext.GetDatabase().BeginTransaction()) { // 1) Download package binary to memory using (var packageStream = await _packageFileService.DownloadPackageFileAsync(package)) { using (var packageArchive = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { // 2) Determine package metadata from binary var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; var packageMetadata = PackageMetadata.FromNuspecReader(packageArchive.GetNuspecReader()); // 3) Clear referenced objects that will be reflowed ClearSupportedFrameworks(package); ClearAuthors(package); ClearDependencies(package); // 4) Reflow the package var listed = package.Listed; package = _packageService.EnrichPackageFromNuGetPackage( package, packageArchive, packageMetadata, packageStreamMetadata, package.User); package.LastEdited = DateTime.UtcNow; package.Listed = listed; // 5) Update IsLatest so that reflow can correct concurrent updates (see Gallery #2514) await _packageService.UpdateIsLatestAsync(package.PackageRegistration, commitChanges : false); // 6) Save and profit await _entitiesContext.SaveChangesAsync(); } } // Commit transaction transaction.Commit(); } EntitiesConfiguration.SuspendExecutionStrategy = false; return(package); }
public async Task <Package> GeneratePackageAsync( string id, PackageArchiveReader nugetPackage, PackageStreamMetadata packageStreamMetadata, User user) { var isPushAllowed = _reservedNamespaceService.IsPushAllowed(id, user, out IReadOnlyCollection <ReservedNamespace> userOwnedNamespaces); var shouldMarkIdVerified = isPushAllowed && userOwnedNamespaces != null && userOwnedNamespaces.Any(); var package = await _packageService.CreatePackageAsync( nugetPackage, packageStreamMetadata, user, isVerified : shouldMarkIdVerified); await _validationService.StartValidationAsync(package); if (shouldMarkIdVerified) { // Add all relevant package registrations to the applicable namespaces foreach (var rn in userOwnedNamespaces) { _reservedNamespaceService.AddPackageRegistrationToNamespace( rn.Value, package.PackageRegistration); } } return(package); }
public async Task SetsPackageStreamMetadataIfChanged() { var content = "Hello, world."; var expectedHash = "rQw3wx1psxXzqB8TyM3nAQlK2RcluhsNwxmcqXE2YbgoDW735o8TPmIR4uWpoxUERddvFwjgRSGw7gNPCwuvJg=="; var stream = new MemoryStream(Encoding.ASCII.GetBytes(content)); PackageStreamMetadata actual = null; PackageFileServiceMock .Setup(x => x.DownloadPackageFileToDiskAsync(Package)) .ReturnsAsync(stream); PackageServiceMock .Setup(x => x.UpdatePackageStreamMetadataAsync(Package, It.IsAny <PackageStreamMetadata>(), false)) .Returns(Task.CompletedTask) .Callback <Package, PackageStreamMetadata, bool>((_, m, __) => actual = m); await Target.SetPackageStatusAsync(Package, ValidationSet, PackageStatus.Available); Assert.NotNull(actual); Assert.Equal(content.Length, actual.Size); Assert.Equal(expectedHash, actual.Hash); Assert.Equal("SHA512", actual.HashAlgorithm); PackageServiceMock.Verify( x => x.UpdatePackageStreamMetadataAsync(Package, actual, false), Times.Once); TelemetryServiceMock.Verify( x => x.TrackDurationToHashPackage( It.Is <TimeSpan>(y => y > TimeSpan.Zero), Package.PackageRegistration.Id, Package.NormalizedVersion, "SHA512", "System.IO.MemoryStream"), Times.Once); }
public virtual async Task UpdatePackageStreamMetadataAsync( Package package, PackageStreamMetadata metadata, bool commitChanges = true) { if (package == null) { throw new ArgumentNullException(nameof(package)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } package.Hash = metadata.Hash; package.HashAlgorithm = metadata.HashAlgorithm; package.PackageFileSize = metadata.Size; var now = DateTime.UtcNow; package.LastUpdated = now; /// If the package is available, consider this change as an "edit" so that the package appears for cursors /// on the <see cref="Package.LastEdited"/> field. if (package.PackageStatusKey == PackageStatus.Available) { package.LastEdited = now; } if (commitChanges) { await _packageRepository.CommitChangesAsync(); } }
/// <remarks> /// This method will create the symbol package entity. The caller should validate the ownership of packages and /// metadata for the symbols associated for this package. Its the caller's responsibility to commit as well. /// </remarks> public SymbolPackage CreateSymbolPackage(Package nugetPackage, PackageStreamMetadata symbolPackageStreamMetadata) { if (nugetPackage == null) { throw new ArgumentNullException(nameof(nugetPackage)); } if (symbolPackageStreamMetadata == null) { throw new ArgumentNullException(nameof(symbolPackageStreamMetadata)); } try { var symbolPackage = new SymbolPackage() { Package = nugetPackage, PackageKey = nugetPackage.Key, Created = DateTime.UtcNow, FileSize = symbolPackageStreamMetadata.Size, HashAlgorithm = symbolPackageStreamMetadata.HashAlgorithm, Hash = symbolPackageStreamMetadata.Hash }; _symbolPackageRepository.InsertOnCommit(symbolPackage); return(symbolPackage); } catch (Exception ex) when(ex is EntityException) { throw new InvalidPackageException(ex.Message, ex); } }
public async Task <Package> GeneratePackageAsync( string id, PackageArchiveReader nugetPackage, PackageStreamMetadata packageStreamMetadata, User owner, User currentUser) { var shouldMarkIdVerified = _reservedNamespaceService.ShouldMarkNewPackageIdVerified(owner, id, out var ownedMatchingReservedNamespaces); var package = await _packageService.CreatePackageAsync( nugetPackage, packageStreamMetadata, owner, currentUser, isVerified : shouldMarkIdVerified); if (shouldMarkIdVerified) { // Add all relevant package registrations to the applicable namespaces foreach (var rn in ownedMatchingReservedNamespaces) { _reservedNamespaceService.AddPackageRegistrationToNamespace( rn.Value, package.PackageRegistration); } } _vulnerabilityService.ApplyExistingVulnerabilitiesToPackage(package); return(package); }
public async Task RejectsNullStreamMetadata() { // Arrange var package = new Package(); PackageStreamMetadata metadata = null; var service = CreateService(); // Act & Assert await Assert.ThrowsAsync <ArgumentNullException>( () => service.UpdatePackageStreamMetadataAsync(package, metadata, commitChanges: true)); }
private async Task <PackageStatus> MarkPackageAsAvailableAsync( PackageValidationSet validationSet, IValidatingEntity <T> validatingEntity, PackageStreamMetadata streamMetadata, bool copied) { // Use whatever package made it into the packages container. This is what customers will consume so the DB // record must match. // We don't immediately commit here. Later, we will commit these changes as well as the new package // status as part of the same transaction. await _galleryPackageService.UpdateMetadataAsync( validatingEntity.EntityRecord, streamMetadata, commitChanges : false); _logger.LogInformation("Marking package {PackageId} {PackageVersion}, validation set {ValidationSetId} as {PackageStatus} in DB", validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, PackageStatus.Available); var fromStatus = validatingEntity.Status; try { // Make the package available and commit any other pending changes (e.g. updated hash). await _galleryPackageService.UpdateStatusAsync(validatingEntity.EntityRecord, PackageStatus.Available, commitChanges : true); } catch (Exception e) { _logger.LogError( Error.UpdatingPackageDbStatusFailed, e, "Failed to update package status in Gallery Db. Package {PackageId} {PackageVersion}, validation set {ValidationSetId}", validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId); // If this execution was not the one to copy the package, then don't delete the package on failure. // This prevents a missing passing in the (unlikely) case where two actors attempt the DB update, one // succeeds and one fails. We don't want an available package record with nothing in the packages // container! if (copied && fromStatus != PackageStatus.Available) { await _packageFileService.DeletePackageFileAsync(validationSet); await OnCleanupAfterDatabaseUpdateFailure(validatingEntity, validationSet); } throw; } return(fromStatus); }
public BaseFacts() { Package = new Package { PackageRegistration = new PackageRegistration(), PackageStatusKey = PackageStatus.Validating, }; ValidationSet = new PackageValidationSet { PackageValidations = new List <PackageValidation> { new PackageValidation { Type = "SomeValidator" }, } }; PackageServiceMock = new Mock <IEntityService <Package> >(); PackageFileServiceMock = new Mock <IValidationFileService>(); ValidatorProviderMock = new Mock <IValidatorProvider>(); TelemetryServiceMock = new Mock <ITelemetryService>(); LoggerMock = new Mock <ILogger <EntityStatusProcessor <Package> > >(); CoreLicenseFileServiceMock = new Mock <ICoreLicenseFileService>(); CoreReadmeFileServiceMock = new Mock <ICoreReadmeFileService>(); var streamMetadata = new PackageStreamMetadata() { Size = 1, Hash = "hash", HashAlgorithm = CoreConstants.Sha512HashAlgorithmId }; SasDefinitionConfiguration = new SasDefinitionConfiguration() { PackageStatusProcessorSasDefinition = "PackageStatusProcessorSasDefinition" }; SasDefinitionConfigurationMock = new Mock <IOptionsSnapshot <SasDefinitionConfiguration> >(); SasDefinitionConfigurationMock.Setup(x => x.Value).Returns(() => SasDefinitionConfiguration); Target = new PackageStatusProcessor( PackageServiceMock.Object, PackageFileServiceMock.Object, ValidatorProviderMock.Object, TelemetryServiceMock.Object, SasDefinitionConfigurationMock.Object, LoggerMock.Object, CoreLicenseFileServiceMock.Object, CoreReadmeFileServiceMock.Object); PackageValidatingEntity = new PackageValidatingEntity(Package); }
public async Task UpdateMetadataAsync(Package entity, object metadata, bool commitChanges = true) { PackageStreamMetadata typedMetadata = metadata == null ? null : metadata as PackageStreamMetadata; if (typedMetadata != null) { if (typedMetadata.Size != entity.PackageFileSize || typedMetadata.Hash != entity.Hash || typedMetadata.HashAlgorithm != entity.HashAlgorithm) { await _galleryEntityService.UpdatePackageStreamMetadataAsync(entity, typedMetadata, commitChanges); } } }
public async Task CommitsTheCorrectNumberOfTimes(bool commitChanges, int commitCount) { // Arrange var packageRepository = new Mock <IEntityRepository <Package> >(); var package = new Package(); var metadata = new PackageStreamMetadata(); var service = CreateService(packageRepository: packageRepository); // Act await service.UpdatePackageStreamMetadataAsync(package, metadata, commitChanges); // Assert packageRepository.Verify(x => x.CommitChangesAsync(), Times.Exactly(commitCount)); }
public BaseFacts() { Package = new Package { PackageRegistration = new PackageRegistration(), PackageStatusKey = PackageStatus.Validating, }; ValidationSet = new PackageValidationSet { PackageValidations = new List <PackageValidation> { new PackageValidation { Type = "SomeValidator" }, } }; PackageServiceMock = new Mock <IEntityService <Package> >(); PackageFileServiceMock = new Mock <IValidationFileService>(); ValidatorProviderMock = new Mock <IValidatorProvider>(); TelemetryServiceMock = new Mock <ITelemetryService>(); LoggerMock = new Mock <ILogger <EntityStatusProcessor <Package> > >(); CoreLicenseFileServiceMock = new Mock <ICoreLicenseFileService>(); var streamMetadata = new PackageStreamMetadata() { Size = 1, Hash = "hash", HashAlgorithm = CoreConstants.Sha512HashAlgorithmId }; PackageFileServiceMock .Setup(x => x.UpdatePackageBlobMetadataAsync(It.IsAny <PackageValidationSet>())) .ReturnsAsync(streamMetadata); Target = new PackageStatusProcessor( PackageServiceMock.Object, PackageFileServiceMock.Object, ValidatorProviderMock.Object, TelemetryServiceMock.Object, LoggerMock.Object, CoreLicenseFileServiceMock.Object); PackageValidatingEntity = new PackageValidatingEntity(Package); }
public async Task DoesNotUpdateLastEditedWhenNotAvailable(PackageStatus packageStatus) { // Arrange var originalLastEdited = new DateTime(2017, 1, 1, 7, 30, 0); var package = new Package { LastEdited = originalLastEdited, PackageStatusKey = packageStatus, }; var metadata = new PackageStreamMetadata(); var service = CreateService(); // Act await service.UpdatePackageStreamMetadataAsync(package, metadata, commitChanges : true); // Assert Assert.Equal(originalLastEdited, package.LastEdited); }
public async Task <PackageStreamMetadata> UpdatePackageBlobMetadataAsync(PackageValidationSet validationSet) { var fileName = BuildFileName( validationSet, _fileMetadataService.FileSavePathTemplate, _fileMetadataService.FileExtension); PackageStreamMetadata streamMetadata = null; // This will throw if the ETag changes between read and write operations, // so streamMetadata will never be null. await _fileStorageService.SetMetadataAsync( _fileMetadataService.FileFolderName, fileName, async (lazyStream, metadata) => { var packageStream = await lazyStream.Value; string hash; using (_telemetryService.TrackDurationToHashPackage( validationSet.PackageId, validationSet.PackageNormalizedVersion, validationSet.ValidationTrackingId, packageStream.Length, CoreConstants.Sha512HashAlgorithmId, packageStream.GetType().FullName)) { hash = CryptographyService.GenerateHash(packageStream, CoreConstants.Sha512HashAlgorithmId); } metadata[CoreConstants.Sha512HashAlgorithmId] = hash; streamMetadata = new PackageStreamMetadata() { Size = packageStream.Length, Hash = hash, HashAlgorithm = CoreConstants.Sha512HashAlgorithmId }; return(true); }); return(streamMetadata); }
public Package CreatePackage(PackageReader nugetPackage, PackageStreamMetadata packageStreamMetadata, User user, bool commitChanges = true) { var packageMetadata = PackageMetadata.FromNuspecReader(nugetPackage.GetNuspecReader()); ValidateNuGetPackageMetadata(packageMetadata); var packageRegistration = CreateOrGetPackageRegistration(user, packageMetadata); var package = CreatePackageFromNuGetPackage(packageRegistration, nugetPackage, packageMetadata, packageStreamMetadata, user); packageRegistration.Packages.Add(package); UpdateIsLatest(packageRegistration, false); if (commitChanges) { _packageRegistrationRepository.CommitChanges(); NotifyIndexingService(); } return(package); }
public void WillThrowInvalidPackageExceptionForDbFail() { // Arrange var symbolPackageStreamMetadata = new PackageStreamMetadata() { Size = 12312, Hash = "01asdf2130", HashAlgorithm = "VerySecureAlogrithm" }; var mockSymbolPackageRepository = new Mock <IEntityRepository <SymbolPackage> >(); mockSymbolPackageRepository .Setup(x => x.InsertOnCommit(It.IsAny <SymbolPackage>())) .Throws(new EntityException("MyException")) .Verifiable(); var service = CreateService(mockSymbolPackageRepository); // Act and Assert Assert.Throws <InvalidPackageException>(() => service.CreateSymbolPackage(new Package(), symbolPackageStreamMetadata)); mockSymbolPackageRepository.Verify(); }
private async Task <bool> IsValidAsync( IPackageService packageService, RequirePackageMetadataState state, string packagePath) { if (!File.Exists(packagePath) && !Directory.Exists(packagePath)) { OutputColor( ConsoleColor.Red, () => { _console.WriteLine("INVALID."); _console.WriteLine(packagePath); _console.WriteLine("The path does not exist."); }); return(false); } if (File.GetAttributes(packagePath).HasFlag(FileAttributes.Directory)) { OutputColor( ConsoleColor.Red, () => { _console.WriteLine("INVALID."); _console.WriteLine(packagePath); _console.WriteLine("The path is a directory, not a file."); }); return(false); } Package package; using (var packageStream = File.OpenRead(packagePath)) { var packageArchiveReader = new PackageArchiveReader(packageStream); var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = CoreConstants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash( packageStream.AsSeekableStream(), CoreConstants.Sha512HashAlgorithmId), Size = packageStream.Length }; var owner = new User(); var currentUser = owner; var isVerified = true; package = await packageService.CreatePackageAsync( packageArchiveReader, packageStreamMetadata, owner, currentUser, isVerified); } var isCompliant = RequirePackageMetadataComplianceUtility.IsPackageMetadataCompliant( package, state, out var complianceFailures); if (isCompliant) { OutputColor( ConsoleColor.Green, () => { _console.WriteLine("VALID."); _console.WriteLine(packagePath); _console.WriteLine($"The package {package.Id} {package.Version} is compliant."); }); return(true); } else { OutputColor( ConsoleColor.Red, () => { var single = complianceFailures.Count == 1; _console.WriteLine("INVALID."); _console.WriteLine(packagePath); _console.WriteLine($"The package {package.Id} {package.Version} is not compliant."); _console.WriteLine($"There {(single ? "is" : "are")} {complianceFailures.Count} problem{(single ? string.Empty : "s")}."); foreach (var failure in complianceFailures) { _console.WriteLine($" - {failure}"); } }); return(false); } }
private async Task <ActionResult> CreatePackageInternal() { var policyResult = await SecurityPolicyService.EvaluateAsync(SecurityPolicyAction.PackagePush, HttpContext); if (!policyResult.Success) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, policyResult.ErrorMessage)); } // Get the user var currentUser = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var archive = new ZipArchive(packageStream, ZipArchiveMode.Read, leaveOpen: true)) { var reference = DateTime.UtcNow.AddDays(1); // allow "some" clock skew var entryInTheFuture = archive.Entries.FirstOrDefault( e => e.LastWriteTime.UtcDateTime > reference); if (entryInTheFuture != null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.PackageEntryFromTheFuture, entryInTheFuture.Name))); } } using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { try { PackageService.EnsureValid(packageToPush); } catch (Exception ex) { ex.Log(); var message = Strings.FailedToReadUploadFile; if (ex is InvalidPackageException || ex is InvalidDataException || ex is EntityException) { message = ex.Message; } return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, message)); } NuspecReader nuspec; var errors = ManifestValidator.Validate(packageToPush.GetNuspec(), out nuspec).ToArray(); if (errors.Length > 0) { var errorsString = string.Join("', '", errors.Select(error => error.ErrorMessage)); return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, errors.Length > 1 ? Strings.UploadPackage_InvalidNuspecMultiple : Strings.UploadPackage_InvalidNuspec, errorsString))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } User owner; // Ensure that the user can push packages for this partialId. var id = nuspec.GetId(); var version = nuspec.GetVersion(); var packageRegistration = PackageService.FindPackageRegistrationById(id); if (packageRegistration == null) { // Check if the current user's scopes allow pushing a new package ID var apiScopeEvaluationResult = EvaluateApiScope(ActionsRequiringPermissions.UploadNewPackageId, new ActionOnNewPackageContext(id, ReservedNamespaceService), NuGetScopes.PackagePush); owner = apiScopeEvaluationResult.Owner; if (!apiScopeEvaluationResult.IsSuccessful()) { // User cannot push a new package ID as the current user's scopes does not allow it return(GetHttpResultFromFailedApiScopeEvaluationForPush(apiScopeEvaluationResult, id, version)); } } else { // Check if the current user's scopes allow pushing a new version of an existing package ID var apiScopeEvaluationResult = EvaluateApiScope(ActionsRequiringPermissions.UploadNewPackageVersion, packageRegistration, NuGetScopes.PackagePushVersion, NuGetScopes.PackagePush); owner = apiScopeEvaluationResult.Owner; if (!apiScopeEvaluationResult.IsSuccessful()) { // User cannot push a package as the current user's scopes does not allow it await AuditingService.SaveAuditRecordAsync( new FailedAuthenticatedOperationAuditRecord( currentUser.Username, AuditedAuthenticatedOperationAction.PackagePushAttemptByNonOwner, attemptedPackage : new AuditedPackageIdentifier( id, version.ToNormalizedStringSafe()))); return(GetHttpResultFromFailedApiScopeEvaluationForPush(apiScopeEvaluationResult, id, version)); } if (packageRegistration.IsLocked) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Forbidden, string.Format(CultureInfo.CurrentCulture, Strings.PackageIsLocked, packageRegistration.Id))); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = version.ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => string.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, id, nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length }; var package = await PackageUploadService.GeneratePackageAsync( id, packageToPush, packageStreamMetadata, owner, currentUser); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); PackageCommitResult commitResult; using (Stream uploadStream = packageStream) { uploadStream.Position = 0; commitResult = await PackageUploadService.CommitPackageAsync( package, uploadStream.AsSeekableStream()); } switch (commitResult) { case PackageCommitResult.Success: break; case PackageCommitResult.Conflict: return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, Strings.UploadPackage_IdVersionConflict)); default: throw new NotImplementedException($"The package commit result {commitResult} is not supported."); } IndexingService.UpdatePackage(package); // Write an audit record await AuditingService.SaveAuditRecordAsync( new PackageAuditRecord(package, AuditedPackageAction.Create, PackageCreatedVia.Api)); if (!(ConfigurationService.Current.AsynchronousPackageValidationEnabled && ConfigurationService.Current.BlockingAsynchronousPackageValidationEnabled)) { // Notify user of push unless async validation in blocking mode is used MessageService.SendPackageAddedNotice(package, Url.Package(package.PackageRegistration.Id, package.NormalizedVersion, relativeUrl: false), Url.ReportPackage(package.PackageRegistration.Id, package.NormalizedVersion, relativeUrl: false), Url.AccountSettings(relativeUrl: false)); } TelemetryService.TrackPackagePushEvent(package, currentUser, User.Identity); if (package.SemVerLevelKey == SemVerLevelKey.SemVer2) { return(new HttpStatusCodeWithServerWarningResult(HttpStatusCode.Created, Strings.WarningSemVer2PackagePushed)); } return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidPackageException ex) { return(BadRequestForExceptionMessage(ex)); } catch (InvalidDataException ex) { return(BadRequestForExceptionMessage(ex)); } catch (EntityException ex) { return(BadRequestForExceptionMessage(ex)); } catch (FrameworkException ex) { return(BadRequestForExceptionMessage(ex)); } } }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var archive = new ZipArchive(packageStream, ZipArchiveMode.Read, leaveOpen: true)) { var reference = DateTime.UtcNow.AddDays(1); // allow "some" clock skew var entryInTheFuture = archive.Entries.FirstOrDefault( e => e.LastWriteTime.UtcDateTime > reference); if (entryInTheFuture != null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.PackageEntryFromTheFuture, entryInTheFuture.Name))); } } using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { NuspecReader nuspec = null; try { nuspec = packageToPush.GetNuspecReader(); } catch (Exception ex) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidNuspec, ex.Message))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(nuspec.GetId()); if (packageRegistration != null) { if (!packageRegistration.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = nuspec.GetVersion().ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => String.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, String.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; var package = await PackageService.CreatePackageAsync( packageToPush, packageStreamMetadata, user, commitChanges : false); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); await EntitiesContext.SaveChangesAsync(); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); IndexingService.UpdatePackage(package); } MessageService.SendPackageAddedNotice(package, Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme)); return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidPackageException ex) { return(BadRequestForExceptionMessage(ex)); } catch (InvalidDataException ex) { return(BadRequestForExceptionMessage(ex)); } catch (EntityException ex) { return(BadRequestForExceptionMessage(ex)); } catch (FrameworkException ex) { return(BadRequestForExceptionMessage(ex)); } } }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { NuspecReader nuspec = null; try { nuspec = packageToPush.GetNuspecReader(); } catch (Exception ex) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidNuspec, ex.Message))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(nuspec.GetId()); if (packageRegistration != null) { if (!packageRegistration.IsOwner(user)) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Forbidden, Strings.ApiKeyNotAuthorized)); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = nuspec.GetVersion().ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => String.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, String.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length, }; var package = await PackageService.CreatePackageAsync(packageToPush, packageStreamMetadata, user, commitChanges : false); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); await EntitiesContext.SaveChangesAsync(); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); IndexingService.UpdatePackage(package); } return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidDataException ex) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.BadRequest, string.Format(CultureInfo.CurrentCulture, Strings.UploadPackage_InvalidPackage, ex.Message))); } } }
/// <summary> /// Validates and creates a <see cref="Package"/> entity from a NuGet package archive. /// </summary> /// <param name="nugetPackage">A <see cref="PackageArchiveReader"/> instance from which package metadata can be read.</param> /// <param name="packageStreamMetadata">The <see cref="PackageStreamMetadata"/> instance providing metadata about the package stream.</param> /// <param name="owner">The <see cref="User"/> creating the package.</param> /// <param name="commitChanges"><c>True</c> to commit the changes to the data store and notify the indexing service; otherwise <c>false</c>.</param> /// <returns>Returns the created <see cref="Package"/> entity.</returns> /// <exception cref="InvalidPackageException"> /// This exception will be thrown when a package metadata property violates a data validation constraint. /// </exception> public async Task <Package> CreatePackageAsync(PackageArchiveReader nugetPackage, PackageStreamMetadata packageStreamMetadata, User owner, User currentUser, bool isVerified) { PackageMetadata packageMetadata; PackageRegistration packageRegistration; try { packageMetadata = PackageMetadata.FromNuspecReader( nugetPackage.GetNuspecReader(), strict: true); ValidateNuGetPackageMetadata(packageMetadata); ValidatePackageTitle(packageMetadata); packageRegistration = CreateOrGetPackageRegistration(owner, currentUser, packageMetadata, isVerified); } catch (Exception exception) when(exception is EntityException || exception is PackagingException) { // Wrap the exception for consistency of this API. throw new InvalidPackageException(exception.Message, exception); } var package = CreatePackageFromNuGetPackage(packageRegistration, nugetPackage, packageMetadata, packageStreamMetadata, currentUser); packageRegistration.Packages.Add(package); await UpdateIsLatestAsync(packageRegistration, commitChanges : false); return(package); }
public virtual Package EnrichPackageFromNuGetPackage( Package package, PackageArchiveReader packageArchive, PackageMetadata packageMetadata, PackageStreamMetadata packageStreamMetadata, User user) { // Version must always be the exact string from the nuspec, which OriginalVersion will return to us. // However, we do also store a normalized copy for looking up later. package.Version = packageMetadata.Version.OriginalVersion; package.NormalizedVersion = packageMetadata.Version.ToNormalizedString(); package.Description = packageMetadata.Description; package.ReleaseNotes = packageMetadata.ReleaseNotes; package.HashAlgorithm = packageStreamMetadata.HashAlgorithm; package.Hash = packageStreamMetadata.Hash; package.PackageFileSize = packageStreamMetadata.Size; package.Language = packageMetadata.Language; package.Copyright = packageMetadata.Copyright; package.FlattenedAuthors = packageMetadata.Authors.Flatten(); package.IsPrerelease = packageMetadata.Version.IsPrerelease; package.Listed = true; package.RequiresLicenseAcceptance = packageMetadata.RequireLicenseAcceptance; package.Summary = packageMetadata.Summary; package.Tags = PackageHelper.ParseTags(packageMetadata.Tags); package.Title = packageMetadata.Title; package.User = user; package.IconUrl = packageMetadata.IconUrl.ToEncodedUrlStringOrNull(); package.LicenseUrl = packageMetadata.LicenseUrl.ToEncodedUrlStringOrNull(); package.ProjectUrl = packageMetadata.ProjectUrl.ToEncodedUrlStringOrNull(); package.MinClientVersion = packageMetadata.MinClientVersion.ToStringOrNull(); #pragma warning disable 618 // TODO: remove Package.Authors completely once production services definitely no longer need it foreach (var author in packageMetadata.Authors) { package.Authors.Add(new PackageAuthor { Name = author }); } #pragma warning restore 618 var supportedFrameworks = GetSupportedFrameworks(packageArchive) .ToArray(); if (!supportedFrameworks.Any(fx => fx != null && fx.IsAny)) { var supportedFrameworkNames = supportedFrameworks .Select(fn => fn.ToShortNameOrNull()) .Where(fn => fn != null) .ToArray(); ValidateSupportedFrameworks(supportedFrameworkNames); foreach (var supportedFramework in supportedFrameworkNames) { package.SupportedFrameworks.Add(new PackageFramework { TargetFramework = supportedFramework }); } } package.Dependencies = packageMetadata .GetDependencyGroups() .AsPackageDependencyEnumerable() .ToList(); package.PackageTypes = packageMetadata .GetPackageTypes() .AsPackageTypeEnumerable() .ToList(); package.FlattenedDependencies = package.Dependencies.Flatten(); package.FlattenedPackageTypes = package.PackageTypes.Flatten(); // Identify the SemVerLevelKey using the original package version string and package dependencies package.SemVerLevelKey = SemVerLevelKey.ForPackage(packageMetadata.Version, package.Dependencies); return(package); }
private Package CreatePackageFromNuGetPackage(PackageRegistration packageRegistration, PackageArchiveReader nugetPackage, PackageMetadata packageMetadata, PackageStreamMetadata packageStreamMetadata, User user) { var package = packageRegistration.Packages.SingleOrDefault(pv => pv.Version == packageMetadata.Version.OriginalVersion); if (package != null) { throw new EntityException( "A package with identifier '{0}' and version '{1}' already exists.", packageRegistration.Id, package.Version); } package = new Package(); package.PackageRegistration = packageRegistration; package = EnrichPackageFromNuGetPackage(package, nugetPackage, packageMetadata, packageStreamMetadata, user); return(package); }
private async Task <ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) { try { using (var archive = new ZipArchive(packageStream, ZipArchiveMode.Read, leaveOpen: true)) { var reference = DateTime.UtcNow.AddDays(1); // allow "some" clock skew var entryInTheFuture = archive.Entries.FirstOrDefault( e => e.LastWriteTime.UtcDateTime > reference); if (entryInTheFuture != null) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.PackageEntryFromTheFuture, entryInTheFuture.Name))); } } using (var packageToPush = new PackageArchiveReader(packageStream, leaveStreamOpen: false)) { try { PackageService.EnsureValid(packageToPush); } catch (Exception ex) { ex.Log(); var message = Strings.FailedToReadUploadFile; if (ex is InvalidPackageException || ex is InvalidDataException || ex is EntityException) { message = ex.Message; } return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, message)); } NuspecReader nuspec; var errors = ManifestValidator.Validate(packageToPush.GetNuspec(), out nuspec).ToArray(); if (errors.Length > 0) { var errorsString = string.Join("', '", errors.Select(error => error.ErrorMessage)); return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, errors.Length > 1 ? Strings.UploadPackage_InvalidNuspecMultiple : Strings.UploadPackage_InvalidNuspec, errorsString))); } if (nuspec.GetMinClientVersion() > Constants.MaxSupportedMinClientVersion) { return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, string.Format( CultureInfo.CurrentCulture, Strings.UploadPackage_MinClientVersionOutOfRange, nuspec.GetMinClientVersion()))); } // Ensure that the user can push packages for this partialId. var packageRegistration = PackageService.FindPackageRegistrationById(nuspec.GetId()); if (packageRegistration == null) { // Check if API key allows pushing a new package id if (!ApiKeyScopeAllows( subject: nuspec.GetId(), requestedActions: NuGetScopes.PackagePush)) { // User cannot push a new package ID as the API key scope does not allow it return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Unauthorized, Strings.ApiKeyNotAuthorized)); } } else { // Is the user allowed to push this Id? if (!packageRegistration.IsOwner(user)) { // Audit that a non-owner tried to push the package await AuditingService.SaveAuditRecord( new FailedAuthenticatedOperationAuditRecord( user.Username, AuditedAuthenticatedOperationAction.PackagePushAttemptByNonOwner, attemptedPackage : new AuditedPackageIdentifier( nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); // User cannot push a package to an ID owned by another user. return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, Strings.PackageIdNotAvailable, nuspec.GetId()))); } // Check if API key allows pushing the current package id if (!ApiKeyScopeAllows( packageRegistration.Id, NuGetScopes.PackagePushVersion, NuGetScopes.PackagePush)) { // User cannot push a package as the API key scope does not allow it return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Unauthorized, Strings.ApiKeyNotAuthorized)); } // Check if a particular Id-Version combination already exists. We eventually need to remove this check. string normalizedVersion = nuspec.GetVersion().ToNormalizedString(); bool packageExists = packageRegistration.Packages.Any( p => string.Equals( p.NormalizedVersion, normalizedVersion, StringComparison.OrdinalIgnoreCase)); if (packageExists) { return(new HttpStatusCodeWithBodyResult( HttpStatusCode.Conflict, string.Format(CultureInfo.CurrentCulture, Strings.PackageExistsAndCannotBeModified, nuspec.GetId(), nuspec.GetVersion().ToNormalizedStringSafe()))); } } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(packageStream.AsSeekableStream()), Size = packageStream.Length }; var package = await PackageService.CreatePackageAsync( packageToPush, packageStreamMetadata, user, commitChanges : false); await AutoCuratePackage.ExecuteAsync(package, packageToPush, commitChanges : false); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; try { await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); } catch (InvalidOperationException ex) { ex.Log(); return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Conflict, Strings.UploadPackage_IdVersionConflict)); } } try { await EntitiesContext.SaveChangesAsync(); } catch { // If saving to the DB fails for any reason, we need to delete the package we just saved. await PackageFileService.DeletePackageFileAsync(nuspec.GetId(), nuspec.GetVersion().ToNormalizedString()); throw; } IndexingService.UpdatePackage(package); // Write an audit record await AuditingService.SaveAuditRecord( new PackageAuditRecord(package, AuditedPackageAction.Create, PackageCreatedVia.Api)); // Notify user of push MessageService.SendPackageAddedNotice(package, Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme)); return(new HttpStatusCodeResult(HttpStatusCode.Created)); } } catch (InvalidPackageException ex) { return(BadRequestForExceptionMessage(ex)); } catch (InvalidDataException ex) { return(BadRequestForExceptionMessage(ex)); } catch (EntityException ex) { return(BadRequestForExceptionMessage(ex)); } catch (FrameworkException ex) { return(BadRequestForExceptionMessage(ex)); } } }
[ValidateInput(false)] // Security note: Disabling ASP.Net input validation which does things like disallow angle brackets in submissions. See http://go.microsoft.com/fwlink/?LinkID=212874 public virtual async Task <ActionResult> VerifyPackage(VerifyPackageRequest formData) { var currentUser = GetCurrentUser(); Package package; using (Stream uploadFile = await _uploadFileService.GetUploadFileAsync(currentUser.Key)) { if (uploadFile == null) { TempData["Message"] = "Your attempt to verify the package submission failed, because we could not find the uploaded package file. Please try again."; return(new RedirectResult(Url.UploadPackage())); } var nugetPackage = await SafeCreatePackage(currentUser, uploadFile); if (nugetPackage == null) { // Send the user back return(new RedirectResult(Url.UploadPackage())); } Debug.Assert(nugetPackage != null); var packageMetadata = PackageMetadata.FromNuspecReader( nugetPackage.GetNuspecReader()); // Rule out problem scenario with multiple tabs - verification request (possibly with edits) was submitted by user // viewing a different package to what was actually most recently uploaded if (!(String.IsNullOrEmpty(formData.Id) || String.IsNullOrEmpty(formData.Version))) { if (!(String.Equals(packageMetadata.Id, formData.Id, StringComparison.OrdinalIgnoreCase) && String.Equals(packageMetadata.Version.ToNormalizedString(), formData.Version, StringComparison.OrdinalIgnoreCase))) { TempData["Message"] = "Your attempt to verify the package submission failed, because the package file appears to have changed. Please try again."; return(new RedirectResult(Url.VerifyPackage())); } } bool pendEdit = false; if (formData.Edit != null) { pendEdit = pendEdit || formData.Edit.RequiresLicenseAcceptance != packageMetadata.RequireLicenseAcceptance; pendEdit = pendEdit || IsDifferent(formData.Edit.IconUrl, packageMetadata.IconUrl.ToEncodedUrlStringOrNull()); pendEdit = pendEdit || IsDifferent(formData.Edit.ProjectUrl, packageMetadata.ProjectUrl.ToEncodedUrlStringOrNull()); pendEdit = pendEdit || IsDifferent(formData.Edit.Authors, packageMetadata.Authors.Flatten()); pendEdit = pendEdit || IsDifferent(formData.Edit.Copyright, packageMetadata.Copyright); pendEdit = pendEdit || IsDifferent(formData.Edit.Description, packageMetadata.Description); pendEdit = pendEdit || IsDifferent(formData.Edit.ReleaseNotes, packageMetadata.ReleaseNotes); pendEdit = pendEdit || IsDifferent(formData.Edit.Summary, packageMetadata.Summary); pendEdit = pendEdit || IsDifferent(formData.Edit.Tags, packageMetadata.Tags); pendEdit = pendEdit || IsDifferent(formData.Edit.VersionTitle, packageMetadata.Title); } var packageStreamMetadata = new PackageStreamMetadata { HashAlgorithm = Constants.Sha512HashAlgorithmId, Hash = CryptographyService.GenerateHash(uploadFile.AsSeekableStream()), Size = uploadFile.Length, }; // update relevant database tables try { package = await _packageService.CreatePackageAsync(nugetPackage, packageStreamMetadata, currentUser, commitChanges : false); Debug.Assert(package.PackageRegistration != null); } catch (EntityException ex) { TempData["Message"] = ex.Message; return(Redirect(Url.UploadPackage())); } await _packageService.PublishPackageAsync(package, commitChanges : false); if (pendEdit) { // Add the edit request to a queue where it will be processed in the background. _editPackageService.StartEditPackageRequest(package, formData.Edit, currentUser); } if (!formData.Listed) { await _packageService.MarkPackageUnlistedAsync(package, commitChanges : false); } await _autoCuratedPackageCmd.ExecuteAsync(package, nugetPackage, commitChanges : false); // save package to blob storage uploadFile.Position = 0; await _packageFileService.SavePackageFileAsync(package, uploadFile.AsSeekableStream()); // commit all changes to database as an atomic transaction await _entitiesContext.SaveChangesAsync(); // tell Lucene to update index for the new package _indexingService.UpdateIndex(); _messageService.SendPackageAddedNotice(package, Url.Action("DisplayPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("ReportMyPackage", "Packages", routeValues: new { id = package.PackageRegistration.Id, version = package.Version }, protocol: Request.Url.Scheme), Url.Action("Account", "Users", routeValues: null, protocol: Request.Url.Scheme)); } // delete the uploaded binary in the Uploads container await _uploadFileService.DeleteUploadFileAsync(currentUser.Key); TempData["Message"] = String.Format( CultureInfo.CurrentCulture, Strings.SuccessfullyUploadedPackage, package.PackageRegistration.Id, package.Version); return(RedirectToRoute(RouteName.DisplayPackage, new { package.PackageRegistration.Id, package.Version })); }