public async Task<Package> ReflowAsync(string id, string version) { var package = _packageService.FindPackageByIdAndVersion(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) Save and profit await _entitiesContext.SaveChangesAsync(); } } // Commit transaction transaction.Commit(); } EntitiesConfiguration.SuspendExecutionStrategy = false; return package; }
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 async Task<ActionResult> CreatePackageInternal() { // Get the user var user = GetCurrentUser(); using (var packageStream = ReadPackageFromRequest()) using (var packageToPush = new PackageReader(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 = PackageService.CreatePackage(packageToPush, packageStreamMetadata, user, commitChanges: false); AutoCuratePackage.Execute(package, packageToPush, commitChanges: false); EntitiesContext.SaveChanges(); using (Stream uploadStream = packageStream) { uploadStream.Position = 0; await PackageFileService.SavePackageFileAsync(package, uploadStream.AsSeekableStream()); IndexingService.UpdatePackage(package); } } return new HttpStatusCodeResult(HttpStatusCode.Created); }
public async Task WillSaveThePackageFileAndSetThePackageFileSize() { var service = CreateService(setup: mockPackageService => { mockPackageService.Setup(x => x.FindPackageRegistrationById(It.IsAny<string>())).Returns((PackageRegistration)null); }); var nugetPackage = CreateNuGetPackage(); var currentUser = new User(); var packageStream = nugetPackage.Object.GetStream().AsSeekableStream(); var packageHash = CryptographyService.GenerateHash(packageStream, Constants.Sha512HashAlgorithmId); var packageStreamMetadata = new PackageStreamMetadata { Hash = packageHash, HashAlgorithm = Constants.Sha512HashAlgorithmId, Size = 618 }; var package = await service.CreatePackageAsync(nugetPackage.Object, packageStreamMetadata, currentUser); Assert.Equal(618, package.PackageFileSize); }
[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 package = _packageService.CreatePackage(nugetPackage, packageStreamMetadata, currentUser, commitChanges: false); Debug.Assert(package.PackageRegistration != null); _packageService.PublishPackage(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) { _packageService.MarkPackageUnlisted(package, commitChanges: false); } _autoCuratedPackageCmd.Execute(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 _entitiesContext.SaveChanges(); // tell Lucene to update index for the new package _indexingService.UpdateIndex(); } // 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 }); }
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 ToString will return to us. // However, we do also store a normalized copy for looking up later. package.Version = packageMetadata.Version.ToString(); 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 prodution 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).Select(fn => fn.ToShortNameOrNull()).ToArray(); if (!supportedFrameworks.AnySafe(sf => sf == null)) { ValidateSupportedFrameworks(supportedFrameworks); foreach (var supportedFramework in supportedFrameworks) { package.SupportedFrameworks.Add(new PackageFramework {TargetFramework = supportedFramework}); } } package.Dependencies = packageMetadata .GetDependencyGroups() .AsPackageDependencyEnumerable() .ToList(); package.FlattenedDependencies = package.Dependencies.Flatten(); 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.ToString()); 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)) { 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); } } }
public void WillStoreTheHashForTheCreatedPackage() { var service = CreateService(setup: mockPackageService => { mockPackageService.Setup(x => x.FindPackageRegistrationById(It.IsAny<string>())).Returns((PackageRegistration)null); }); var nugetPackage = CreateNuGetPackage(); var currentUser = new User(); var packageStream = nugetPackage.Object.GetStream().AsSeekableStream(); var expectedHash = CryptographyService.GenerateHash(packageStream, Constants.Sha512HashAlgorithmId); var packageStreamMetadata = new PackageStreamMetadata { Hash = expectedHash, HashAlgorithm = Constants.Sha512HashAlgorithmId, Size = packageStream.Length }; var package = service.CreatePackage(nugetPackage.Object, packageStreamMetadata, currentUser); Assert.Equal(expectedHash, package.Hash); Assert.Equal(Constants.Sha512HashAlgorithmId, package.HashAlgorithm); }
private Package CreatePackageFromNuGetPackage(PackageRegistration packageRegistration, PackageArchiveReader nugetPackage, PackageMetadata packageMetadata, PackageStreamMetadata packageStreamMetadata, User user) { var package = packageRegistration.Packages.SingleOrDefault(pv => pv.Version == packageMetadata.Version.ToString()); if (package != null) { throw new EntityException( "A package with identifier '{0}' and version '{1}' already exists.", packageRegistration.Id, package.Version); } package = new Package { // Version must always be the exact string from the nuspec, which ToString will return to us. // However, we do also store a normalized copy for looking up later. Version = packageMetadata.Version.ToString(), NormalizedVersion = packageMetadata.Version.ToNormalizedString(), Description = packageMetadata.Description, ReleaseNotes = packageMetadata.ReleaseNotes, HashAlgorithm = packageStreamMetadata.HashAlgorithm, Hash = packageStreamMetadata.Hash, PackageFileSize = packageStreamMetadata.Size, Language = packageMetadata.Language, Copyright = packageMetadata.Copyright, FlattenedAuthors = packageMetadata.Authors.Flatten(), IsPrerelease = packageMetadata.Version.IsPrerelease, Listed = true, PackageRegistration = packageRegistration, RequiresLicenseAcceptance = packageMetadata.RequireLicenseAcceptance, Summary = packageMetadata.Summary, Tags = PackageHelper.ParseTags(packageMetadata.Tags), Title = packageMetadata.Title, 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 prodution 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(nugetPackage).Select(fn => fn.ToShortNameOrNull()).ToArray(); if (!supportedFrameworks.AnySafe(sf => sf == null)) { ValidateSupportedFrameworks(supportedFrameworks); foreach (var supportedFramework in supportedFrameworks) { package.SupportedFrameworks.Add(new PackageFramework { TargetFramework = supportedFramework }); } } package.Dependencies = packageMetadata .GetDependencyGroups() .AsPackageDependencyEnumerable() .ToList(); package.FlattenedDependencies = package.Dependencies.Flatten(); return package; }