/// <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); }
/// <summary> /// Save the readme file from package stream. This method should throw if the package /// does not have an embedded readme file /// </summary> /// <param name="package">Package information.</param> /// <param name="packageStream">Package stream with .nupkg contents.</param> public async Task ExtractAndSaveReadmeFileAsync(Package package, Stream packageStream) { if (package == null) { throw new ArgumentNullException(nameof(package)); } if (packageStream == null) { throw new ArgumentNullException(nameof(packageStream)); } packageStream.Seek(0, SeekOrigin.Begin); using (var packageArchiveReader = new PackageArchiveReader(packageStream, leaveStreamOpen: true)) { var packageMetadata = PackageMetadata.FromNuspecReader(packageArchiveReader.GetNuspecReader(), strict: true); if (string.IsNullOrWhiteSpace(packageMetadata.ReadmeFile)) { throw new InvalidOperationException("No readme file specified in the nuspec"); } var filename = FileNameHelper.GetZipEntryPath(packageMetadata.ReadmeFile); var ReadmeFileEntry = packageArchiveReader.GetEntry(filename); // throws on non-existent file using (var readmeFileStream = ReadmeFileEntry.Open()) { await SaveReadmeFileAsync(package, readmeFileStream); } } }
/// <summary> /// When no exceptions thrown, this method ensures the symbol package metadata is valid. /// </summary> /// <param name="symbolPackageArchiveReader"> /// The <see cref="PackageArchiveReader"/> instance providing the package metadata. /// </param> /// <exception cref="InvalidPackageException"> /// This exception will be thrown when a package metadata property violates a data validation constraint. /// </exception> public async Task EnsureValidAsync(PackageArchiveReader symbolPackageArchiveReader) { if (symbolPackageArchiveReader == null) { throw new ArgumentNullException(nameof(symbolPackageArchiveReader)); } // Validate following checks: // 1. Is a symbol package. // 2. The nuspec shouldn't have the 'owners'/'authors' field. // 3. All files have pdb extensions. // 4. Other package manifest validations. try { var packageMetadata = PackageMetadata.FromNuspecReader( symbolPackageArchiveReader.GetNuspecReader(), strict: true); if (!packageMetadata.IsSymbolsPackage()) { throw new InvalidPackageException(Strings.SymbolsPackage_NotSymbolPackage); } ValidateSymbolPackage(symbolPackageArchiveReader, packageMetadata); // This will throw if the package contains an entry which will extract outside of the target extraction directory await symbolPackageArchiveReader.ValidatePackageEntriesAsync(CancellationToken.None); } catch (Exception ex) when(ex is EntityException || ex is PackagingException) { // Wrap the exception for consistency of this API. throw new InvalidPackageException(ex.Message, ex); } }
/// <summary> /// When no exceptions thrown, this method ensures the package metadata is valid. /// </summary> /// <param name="packageArchiveReader"> /// The <see cref="PackageArchiveReader"/> instance providing the package metadata. /// </param> /// <exception cref="InvalidPackageException"> /// This exception will be thrown when a package metadata property violates a data validation constraint. /// </exception> public async Task EnsureValid(PackageArchiveReader packageArchiveReader) { try { var packageMetadata = PackageMetadata.FromNuspecReader( packageArchiveReader.GetNuspecReader(), strict: true); if (packageMetadata.IsSymbolsPackage()) { throw new InvalidPackageException(ServicesStrings.UploadPackage_SymbolsPackageNotAllowed); } PackageHelper.ValidateNuGetPackageMetadata(packageMetadata); var supportedFrameworks = GetSupportedFrameworks(packageArchiveReader).Select(fn => fn.ToShortNameOrNull()).ToArray(); if (!supportedFrameworks.AnySafe(sf => sf == null)) { ValidateSupportedFrameworks(supportedFrameworks); } // This will throw if the package contains an entry which will extract outside of the target extraction directory await packageArchiveReader.ValidatePackageEntriesAsync(CancellationToken.None); } catch (Exception exception) when(exception is EntityException || exception is PackagingException) { // Wrap the exception for consistency of this API. throw new InvalidPackageException(exception.Message, exception); } }
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); }
/// <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="user">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 user, bool commitChanges = true) { PackageMetadata packageMetadata; PackageRegistration packageRegistration; try { packageMetadata = PackageMetadata.FromNuspecReader(nugetPackage.GetNuspecReader()); ValidateNuGetPackageMetadata(packageMetadata); ValidatePackageTitle(packageMetadata); packageRegistration = CreateOrGetPackageRegistration(user, packageMetadata); } 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, user); packageRegistration.Packages.Add(package); await UpdateIsLatestAsync(packageRegistration, false); if (commitChanges) { await _packageRegistrationRepository.CommitChangesAsync(); NotifyIndexingService(); } return(package); }
public virtual async Task <ActionResult> VerifyPackage() { var currentUser = GetCurrentUser(); PackageMetadata packageMetadata; using (Stream uploadFile = await _uploadFileService.GetUploadFileAsync(currentUser.Key)) { if (uploadFile == null) { return(RedirectToRoute(RouteName.UploadPackage)); } var package = await SafeCreatePackage(currentUser, uploadFile); if (package == null) { return(Redirect(Url.UploadPackage())); } try { packageMetadata = PackageMetadata.FromNuspecReader( package.GetNuspecReader()); } catch (Exception ex) { TempData["Message"] = ex.GetUserSafeMessage(); return(Redirect(Url.UploadPackage())); } } var model = new VerifyPackageRequest { Id = packageMetadata.Id, Version = packageMetadata.Version.ToNormalizedStringSafe(), LicenseUrl = packageMetadata.LicenseUrl.ToEncodedUrlStringOrNull(), Listed = true, Edit = new EditPackageVersionRequest { Authors = packageMetadata.Authors.Flatten(), Copyright = packageMetadata.Copyright, Description = packageMetadata.Description, IconUrl = packageMetadata.IconUrl.ToEncodedUrlStringOrNull(), LicenseUrl = packageMetadata.LicenseUrl.ToEncodedUrlStringOrNull(), ProjectUrl = packageMetadata.ProjectUrl.ToEncodedUrlStringOrNull(), ReleaseNotes = packageMetadata.ReleaseNotes, RequiresLicenseAcceptance = packageMetadata.RequireLicenseAcceptance, Summary = packageMetadata.Summary, Tags = PackageHelper.ParseTags(packageMetadata.Tags), VersionTitle = packageMetadata.Title, } }; return(View(model)); }
private static PackageUploadService CreateService( Mock <IPackageService> packageService = null, Mock <IReservedNamespaceService> reservedNamespaceService = null, Mock <IValidationService> validationService = null) { if (packageService == null) { packageService = new Mock <IPackageService>(); } packageService.Setup(x => x.FindPackageRegistrationById(It.IsAny <string>())).Returns((PackageRegistration)null); packageService.Setup(x => x .CreatePackageAsync(It.IsAny <PackageArchiveReader>(), It.IsAny <PackageStreamMetadata>(), It.IsAny <User>(), It.IsAny <User>(), It.IsAny <bool>())) .Returns((PackageArchiveReader packageArchiveReader, PackageStreamMetadata packageStreamMetadata, User owner, User currentUser, bool isVerified) => { var packageMetadata = PackageMetadata.FromNuspecReader( packageArchiveReader.GetNuspecReader(), strict: true); var newPackage = new Package(); newPackage.PackageRegistration = new PackageRegistration { Id = packageMetadata.Id, IsVerified = isVerified }; newPackage.Version = packageMetadata.Version.ToString(); newPackage.SemVerLevelKey = SemVerLevelKey.ForPackage(packageMetadata.Version, packageMetadata.GetDependencyGroups().AsPackageDependencyEnumerable()); return(Task.FromResult(newPackage)); }); if (reservedNamespaceService == null) { reservedNamespaceService = new Mock <IReservedNamespaceService>(); reservedNamespaceService .Setup(r => r.GetReservedNamespacesForId(It.IsAny <string>())) .Returns(new ReservedNamespace[0]); } if (validationService == null) { validationService = new Mock <IValidationService>(); } var packageUploadService = new Mock <PackageUploadService>( packageService.Object, new Mock <IPackageFileService>().Object, new Mock <IEntitiesContext>().Object, reservedNamespaceService.Object, validationService.Object); return(packageUploadService.Object); }
public void EnsureValid(PackageReader packageReader) { var packageMetadata = PackageMetadata.FromNuspecReader(packageReader.GetNuspecReader()); ValidateNuGetPackageMetadata(packageMetadata); var supportedFrameworks = GetSupportedFrameworks(packageReader).Select(fn => fn.ToShortNameOrNull()).ToArray(); if (!supportedFrameworks.AnySafe(sf => sf == null)) { ValidateSupportedFrameworks(supportedFrameworks); } }
private async Task SavePackageLicenseFile(Stream packageFile, Package package) { packageFile.Seek(0, SeekOrigin.Begin); using (var packageArchiveReader = new PackageArchiveReader(packageFile, leaveStreamOpen: true)) { var packageMetadata = PackageMetadata.FromNuspecReader(packageArchiveReader.GetNuspecReader(), strict: true); if (packageMetadata.LicenseMetadata == null || packageMetadata.LicenseMetadata.Type != LicenseType.File || string.IsNullOrWhiteSpace(packageMetadata.LicenseMetadata.License)) { throw new InvalidOperationException("No license file specified in the nuspec"); } var filename = packageMetadata.LicenseMetadata.License; var licenseFileEntry = packageArchiveReader.GetEntry(filename); // throws on non-existent file using (var licenseFileStream = licenseFileEntry.Open()) { await _packageFileService.SaveLicenseFileAsync(package, licenseFileStream); } } }
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); }
private static PackageUploadService CreateService( Mock <IPackageService> packageService = null, Mock <IReservedNamespaceService> reservedNamespaceService = null) { { if (packageService == null) { packageService = new Mock <IPackageService>(); } packageService.Setup(x => x.FindPackageRegistrationById(It.IsAny <string>())).Returns((PackageRegistration)null); packageService.Setup(x => x.CreatePackageAsync(It.IsAny <PackageArchiveReader>(), It.IsAny <PackageStreamMetadata>(), It.IsAny <User>(), It.IsAny <bool>(), It.IsAny <bool>())). Returns((PackageArchiveReader packageArchiveReader, PackageStreamMetadata packageStreamMetadata, User user, bool isVerified, bool commitChanges) => { var packageMetadata = PackageMetadata.FromNuspecReader(packageArchiveReader.GetNuspecReader()); var newPackage = new Package(); newPackage.PackageRegistration = new PackageRegistration { Id = packageMetadata.Id, IsVerified = isVerified }; newPackage.Version = packageMetadata.Version.ToString(); newPackage.SemVerLevelKey = SemVerLevelKey.ForPackage(packageMetadata.Version, packageMetadata.GetDependencyGroups().AsPackageDependencyEnumerable()); return(Task.FromResult(newPackage)); }); } if (reservedNamespaceService == null) { reservedNamespaceService = new Mock <IReservedNamespaceService>(); IReadOnlyCollection <ReservedNamespace> userOwnedMatchingNamespaces = new List <ReservedNamespace>(); reservedNamespaceService.Setup(s => s.IsPushAllowed(It.IsAny <string>(), It.IsAny <User>(), out userOwnedMatchingNamespaces)) .Returns(true); } var packageUploadService = new Mock <PackageUploadService>( packageService.Object, reservedNamespaceService.Object); return(packageUploadService.Object); }
/// <summary> /// When no exceptions thrown, this method ensures the package metadata is valid. /// </summary> /// <param name="packageArchiveReader"> /// The <see cref="PackageArchiveReader"/> instance providing the package metadata. /// </param> /// <exception cref="InvalidPackageException"> /// This exception will be thrown when a package metadata property violates a data validation constraint. /// </exception> public void EnsureValid(PackageArchiveReader packageArchiveReader) { try { var packageMetadata = PackageMetadata.FromNuspecReader(packageArchiveReader.GetNuspecReader()); ValidateNuGetPackageMetadata(packageMetadata); ValidatePackageTitle(packageMetadata); var supportedFrameworks = GetSupportedFrameworks(packageArchiveReader).Select(fn => fn.ToShortNameOrNull()).ToArray(); if (!supportedFrameworks.AnySafe(sf => sf == null)) { ValidateSupportedFrameworks(supportedFrameworks); } } catch (Exception exception) when(exception is EntityException || exception is PackagingException) { // Wrap the exception for consistency of this API. throw new InvalidPackageException(exception.Message, exception); } }
public async Task <Package> CreatePackageAsync(PackageArchiveReader nugetPackage, PackageStreamMetadata packageStreamMetadata, User user, bool commitChanges = true) { var packageMetadata = PackageMetadata.FromNuspecReader(nugetPackage.GetNuspecReader()); ValidateNuGetPackageMetadata(packageMetadata); ValidatePackageTitle(packageMetadata); var packageRegistration = CreateOrGetPackageRegistration(user, packageMetadata); var package = CreatePackageFromNuGetPackage(packageRegistration, nugetPackage, packageMetadata, packageStreamMetadata, user); packageRegistration.Packages.Add(package); if (commitChanges) { await _packageRegistrationRepository.CommitChangesAsync(); NotifyIndexingService(); } return(package); }
[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 })); }
private PackageMetadata GetPackageMetadata(Mock <TestPackageReader> mockPackage) { return(PackageMetadata.FromNuspecReader(mockPackage.Object.GetNuspecReader(), strict: true)); }