示例#1
0
        public async virtual Task <ActionResult> VerifyPackageKeyAsync(string id, string version)
        {
            var policyResult = await SecurityPolicyService.EvaluateAsync(SecurityPolicyAction.PackageVerify, HttpContext);

            if (!policyResult.Success)
            {
                return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, policyResult.ErrorMessage));
            }

            var user       = GetCurrentUser();
            var credential = user.GetCurrentApiKeyCredential(User.Identity);

            var result = await VerifyPackageKeyInternalAsync(user, credential, id, version);

            // Expire and delete verification key after first use to avoid growing the database tables.
            if (CredentialTypes.IsPackageVerificationApiKey(credential.Type))
            {
                await AuthenticationService.RemoveCredential(user, credential);
            }

            TelemetryService.TrackVerifyPackageKeyEvent(id, version, user, User.Identity, result?.StatusCode ?? 200);

            return((ActionResult)result ?? new EmptyResult());
        }
示例#2
0
        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()
        {
            var policyResult = await SecurityPolicyService.EvaluateAsync(SecurityPolicyAction.PackagePush, HttpContext);

            if (!policyResult.Success)
            {
                return(new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, policyResult.ErrorMessage));
            }

            // 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 id = nuspec.GetId();
                        var packageRegistration = PackageService.FindPackageRegistrationById(id);
                        IReadOnlyCollection <ReservedNamespace> userOwnedNamespaces = null;
                        if (packageRegistration == null)
                        {
                            // Check if API key allows pushing a new package id
                            if (!ApiKeyScopeAllows(
                                    subject: id,
                                    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));
                            }

                            // For a new package id verify that the user is allowed to push to the matching namespaces, if any.
                            var isPushAllowed = ReservedNamespaceService.IsPushAllowed(id, user, out userOwnedNamespaces);
                            if (!isPushAllowed)
                            {
                                return(new HttpStatusCodeWithBodyResult(HttpStatusCode.Conflict, Strings.UploadPackage_IdNamespaceConflict));
                            }
                        }
                        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.SaveAuditRecordAsync(
                                    new FailedAuthenticatedOperationAuditRecord(
                                        user.Username,
                                        AuditedAuthenticatedOperationAction.PackagePushAttemptByNonOwner,
                                        attemptedPackage : new AuditedPackageIdentifier(
                                            id, 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,
                                                                                      id)));
                            }

                            // 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,
                                                         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,
                            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.SaveAuditRecordAsync(
                            new PackageAuditRecord(package, AuditedPackageAction.Create, PackageCreatedVia.Api));

                        // Notify user of push
                        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, user, 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));
                }
            }
        }