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;
        }
Example #2
0
        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;
        }
Example #3
0
        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;
        }
Example #8
0
        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);
            }
Example #10
0
        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;
        }