public async Task AddPackageOwnerAsync(PackageRegistration packageRegistration, User user, bool commitChanges = true)
        {
            if (packageRegistration == null)
            {
                throw new ArgumentNullException(nameof(packageRegistration));
            }

            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            if (commitChanges)
            {
                using (var strategy = new SuspendDbExecutionStrategy())
                    using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                    {
                        await AddPackageOwnerTask(packageRegistration, user, commitChanges);

                        transaction.Commit();
                    }
            }
            else
            {
                await AddPackageOwnerTask(packageRegistration, user, commitChanges);
            }

            await _auditingService.SaveAuditRecordAsync(
                new PackageRegistrationAuditRecord(packageRegistration, AuditedPackageRegistrationAction.AddOwner, user.Username));
        }
Ejemplo n.º 2
0
        public async Task MarkPackageListedAsync(Package package, bool commitChanges = true)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            if (package.Listed)
            {
                return;
            }

            if (package.PackageStatusKey == PackageStatus.Deleted)
            {
                throw new InvalidOperationException("A deleted package should never be listed!");
            }
            if (!package.Listed && (package.IsLatestStable || package.IsLatest))
            {
                throw new InvalidOperationException("An unlisted package should never be latest or latest stable!");
            }

            package.Listed      = true;
            package.LastUpdated = DateTime.UtcNow;
            // NOTE: LastEdited will be overwritten by a trigger defined in the migration named "AddTriggerForPackagesLastEdited".
            package.LastEdited = DateTime.UtcNow;

            await UpdateIsLatestAsync(package.PackageRegistration, commitChanges : false);

            await _auditingService.SaveAuditRecordAsync(new PackageAuditRecord(package, AuditedPackageAction.List));

            if (commitChanges)
            {
                await _packageRepository.CommitChangesAsync();
            }
        }
Ejemplo n.º 3
0
        public async Task SoftDeletePackagesAsync(IEnumerable <Package> packages, User deletedBy, string reason, string signature)
        {
            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    // Increase command timeout
                    _entitiesContext.SetCommandTimeout(seconds: 300);

                    // Keep package registrations
                    var packageRegistrations = packages
                                               .GroupBy(p => p.PackageRegistration)
                                               .Select(g => g.First().PackageRegistration)
                                               .ToList();

                    // Backup the package binaries and remove from main storage
                    // We're doing this early in the process as we need the metadata to still exist in the DB.
                    await BackupPackageBinaries(packages);

                    // Store the soft delete in the database
                    var packageDelete = new PackageDelete
                    {
                        DeletedOn = DateTime.UtcNow,
                        DeletedBy = deletedBy,
                        Reason    = reason,
                        Signature = signature
                    };

                    foreach (var package in packages)
                    {
                        /// We do not call <see cref="IPackageService.MarkPackageUnlistedAsync(Package, bool)"/> here
                        /// because that writes an audit entry. Additionally, the latest bits are already updated by
                        /// the package status change.
                        package.Listed = false;

                        await _packageService.UpdatePackageStatusAsync(
                            package,
                            PackageStatus.Deleted,
                            commitChanges : false);

                        packageDelete.Packages.Add(package);

                        await _auditingService.SaveAuditRecordAsync(CreateAuditRecord(package, package.PackageRegistration, AuditedPackageAction.SoftDelete, reason));

                        _telemetryService.TrackPackageDelete(package, isHardDelete: false);
                    }

                    _packageDeletesRepository.InsertOnCommit(packageDelete);

                    // Commit changes
                    await _packageRepository.CommitChangesAsync();

                    await _packageDeletesRepository.CommitChangesAsync();

                    transaction.Commit();
                }

            // Force refresh the index
            UpdateSearchIndex();
        }
        public async Task SoftDeletePackagesAsync(IEnumerable <Package> packages, User deletedBy, string reason, string signature)
        {
            List <PackageRegistration> packageRegistrations;

            // Must suspend the retry execution strategy in order to use transactions.
            using (EntitiesConfiguration.SuspendRetriableExecutionStrategy())
            {
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    // Increase command timeout
                    _entitiesContext.SetCommandTimeout(seconds: 300);

                    // Keep package registrations
                    packageRegistrations = packages
                                           .GroupBy(p => p.PackageRegistration)
                                           .Select(g => g.First().PackageRegistration)
                                           .ToList();

                    // Backup the package binaries and remove from main storage
                    // We're doing this early in the process as we need the metadata to still exist in the DB.
                    await BackupPackageBinaries(packages);

                    // Store the soft delete in the database
                    var packageDelete = new PackageDelete
                    {
                        DeletedOn = DateTime.UtcNow,
                        DeletedBy = deletedBy,
                        Reason    = reason,
                        Signature = signature
                    };

                    foreach (var package in packages)
                    {
                        package.Listed  = false;
                        package.Deleted = true;
                        packageDelete.Packages.Add(package);

                        await _auditingService.SaveAuditRecordAsync(CreateAuditRecord(package, package.PackageRegistration, AuditedPackageAction.SoftDelete, reason));
                    }

                    _packageDeletesRepository.InsertOnCommit(packageDelete);

                    // Commit changes
                    await _packageRepository.CommitChangesAsync();

                    await _packageDeletesRepository.CommitChangesAsync();

                    transaction.Commit();
                }
            }

            // handle in separate transaction because of concurrency check with retry
            await UpdateIsLatestAsync(packageRegistrations);

            // Force refresh the index
            UpdateSearchIndex();
        }
        /// <summary>
        /// Asynchronously sets the signer as the required signer on all package registrations owned by the signer.
        /// </summary>
        /// <param name="signer">A user.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="signer" /> is <c>null</c>.</exception>
        public async Task SetRequiredSignerAsync(User signer)
        {
            if (signer == null)
            {
                throw new ArgumentNullException(nameof(signer));
            }

            var registrations    = FindPackageRegistrationsByOwner(signer);
            var auditRecords     = new List <PackageRegistrationAuditRecord>();
            var packageIds       = new List <string>();
            var isCommitRequired = false;

            foreach (var registration in registrations)
            {
                string previousRequiredSigner = null;
                string newRequiredSigner      = null;

                if (!registration.RequiredSigners.Contains(signer))
                {
                    previousRequiredSigner = registration.RequiredSigners.FirstOrDefault()?.Username;

                    registration.RequiredSigners.Clear();

                    isCommitRequired = true;

                    registration.RequiredSigners.Add(signer);

                    newRequiredSigner = signer.Username;

                    var auditRecord = PackageRegistrationAuditRecord.CreateForSetRequiredSigner(
                        registration,
                        previousRequiredSigner,
                        newRequiredSigner);

                    auditRecords.Add(auditRecord);
                    packageIds.Add(registration.Id);
                }
            }

            if (isCommitRequired)
            {
                await _packageRegistrationRepository.CommitChangesAsync();

                foreach (var auditRecord in auditRecords)
                {
                    await _auditingService.SaveAuditRecordAsync(auditRecord);
                }

                foreach (var packageId in packageIds)
                {
                    _telemetryService.TrackRequiredSignerSet(packageId);
                }
            }
        }
        public async Task AddPackageOwnerAsync(PackageRegistration packageRegistration, User user)
        {
            if (packageRegistration == null)
            {
                throw new ArgumentNullException(nameof(packageRegistration));
            }

            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    Func <ReservedNamespace, bool> predicate =
                        reservedNamespace => reservedNamespace.IsPrefix
                        ? packageRegistration.Id.StartsWith(reservedNamespace.Value, StringComparison.OrdinalIgnoreCase)
                        : packageRegistration.Id.Equals(reservedNamespace.Value, StringComparison.OrdinalIgnoreCase);

                    var userOwnedMatchingNamespacesForId = user
                                                           .ReservedNamespaces
                                                           .Where(predicate);

                    if (userOwnedMatchingNamespacesForId.Any())
                    {
                        if (!packageRegistration.IsVerified)
                        {
                            await _packageService.UpdatePackageVerifiedStatusAsync(new List <PackageRegistration> {
                                packageRegistration
                            }, isVerified : true);
                        }

                        userOwnedMatchingNamespacesForId
                        .ToList()
                        .ForEach(mn =>
                                 _reservedNamespaceService.AddPackageRegistrationToNamespace(mn.Value, packageRegistration));

                        // The 'AddPackageRegistrationToNamespace' does not commit its changes, so saving changes for consistency.
                        await _entitiesContext.SaveChangesAsync();
                    }

                    await _packageService.AddPackageOwnerAsync(packageRegistration, user);

                    await DeletePackageOwnershipRequestAsync(packageRegistration, user);

                    transaction.Commit();
                }

            await _auditingService.SaveAuditRecordAsync(
                new PackageRegistrationAuditRecord(packageRegistration, AuditedPackageRegistrationAction.AddOwner, user.Username));
        }
Ejemplo n.º 7
0
        public async Task AddPackageOwnerAsync(PackageRegistration package, User newOwner)
        {
            package.Owners.Add(newOwner);
            await _packageRepository.CommitChangesAsync();

            var request = _packageOwnerRequestService.GetPackageOwnershipRequests(package: package, newOwner: newOwner).FirstOrDefault();

            if (request != null)
            {
                await _packageOwnerRequestService.DeletePackageOwnershipRequest(request);
            }

            await _auditingService.SaveAuditRecordAsync(
                new PackageRegistrationAuditRecord(package, AuditedPackageRegistrationAction.AddOwner, newOwner.Username));
        }
Ejemplo n.º 8
0
        public async Task AddPackageOwnerAsync(PackageRegistration package, User newOwner)
        {
            package.Owners.Add(newOwner);
            await _packageRepository.CommitChangesAsync();

            var request = FindExistingPackageOwnerRequest(package, newOwner);
            if (request != null)
            {
                _packageOwnerRequestRepository.DeleteOnCommit(request);
                await _packageOwnerRequestRepository.CommitChangesAsync();
            }

            await _auditingService.SaveAuditRecordAsync(
                new PackageRegistrationAuditRecord(package, AuditedPackageRegistrationAction.AddOwner, newOwner.Username));
        }
Ejemplo n.º 9
0
        public async Task MarkPackageListedAsync(Package package, bool commitChanges = true, bool updateIndex = true)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            if (package.Listed)
            {
                return;
            }

            if (package.PackageStatusKey == PackageStatus.Deleted)
            {
                throw new InvalidOperationException("A deleted package should never be listed!");
            }

            if (package.PackageStatusKey == PackageStatus.FailedValidation)
            {
                throw new InvalidOperationException("A package that failed validation should never be listed!");
            }

            if (!package.Listed && (package.IsLatestStable || package.IsLatest || package.IsLatestSemVer2 || package.IsLatestStableSemVer2))
            {
                throw new InvalidOperationException("An unlisted package should never be latest or latest stable!");
            }

            package.Listed = true;
            package.LastUpdated = DateTime.UtcNow;
            // NOTE: LastEdited will be overwritten by a trigger defined in the migration named "AddTriggerForPackagesLastEdited".
            package.LastEdited = DateTime.UtcNow;

            await _packageService.UpdateIsLatestAsync(package.PackageRegistration, commitChanges: false);

            await _auditingService.SaveAuditRecordAsync(new PackageAuditRecord(package, AuditedPackageAction.List));

            _telemetryService.TrackPackageListed(package);

            if (commitChanges)
            {
                await _entitiesContext.SaveChangesAsync();
            }

            if (updateIndex)
            {
                _indexingService.UpdatePackage(package);
            }
        }
Ejemplo n.º 10
0
        /// <summary>
        /// On subscribe, set API keys with push capability to expire in <see cref="PushKeysExpirationInDays" /> days.
        /// </summary>
        /// <param name="context"></param>
        public async Task OnSubscribeAsync(UserSecurityPolicySubscriptionContext context)
        {
            var pushKeys = context.User.Credentials.Where(c =>
                                                          CredentialTypes.IsApiKey(c.Type) &&
                                                          (
                                                              c.Scopes.Count == 0 ||
                                                              c.Scopes.Any(s =>
                                                                           s.AllowedAction.Equals(NuGetScopes.PackagePush, StringComparison.OrdinalIgnoreCase) ||
                                                                           s.AllowedAction.Equals(NuGetScopes.PackagePushVersion, StringComparison.OrdinalIgnoreCase)
                                                                           ))
                                                          );

            var expires     = DateTime.UtcNow.AddDays(PushKeysExpirationInDays);
            var expireTasks = new List <Task>();

            foreach (var key in pushKeys)
            {
                if (!key.Expires.HasValue || key.Expires > expires)
                {
                    expireTasks.Add(_auditing.SaveAuditRecordAsync(
                                        new UserAuditRecord(context.User, AuditedUserAction.ExpireCredential, key)));

                    key.Expires = expires;
                }
            }
            await Task.WhenAll(expireTasks);

            _diagnostics.Information($"Expiring {pushKeys.Count()} keys with push capability for user '{context.User.Username}'.");
        }
Ejemplo n.º 11
0
        private async Task <DeleteUserAccountStatus> RunAccountDeletionTask(Func <Task> getTask, User userToBeDeleted, User requestingUser, bool commitAsTransaction)
        {
            try
            {
                // The support requests DB and gallery DB are different.
                // TransactionScope can be used for doing transaction actions across db on the same server but not on different servers.
                // The below code will clean the feature flags and suppport requests before the gallery data.
                // The order is important in order to allow the admin the opportunity to execute this step again.
                await _featureFlagService.RemoveUserAsync(userToBeDeleted);
                await RemoveSupportRequests(userToBeDeleted);

                if (commitAsTransaction)
                {
                    using (var strategy = new SuspendDbExecutionStrategy())
                        using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                        {
                            await getTask();

                            transaction.Commit();
                        }
                }
                else
                {
                    await getTask();
                }

                await _auditingService.SaveAuditRecordAsync(new DeleteAccountAuditRecord(username : userToBeDeleted.Username,
                                                                                         status : DeleteAccountAuditRecord.ActionStatus.Success,
                                                                                         action : AuditedDeleteAccountAction.DeleteAccount,
                                                                                         adminUsername : requestingUser.Username));

                return(new DeleteUserAccountStatus()
                {
                    Success = true,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_Success,
                                                userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }
            catch (Exception e)
            {
                QuietLog.LogHandledException(e);
                return(new DeleteUserAccountStatus()
                {
                    Success = false,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_Fail,
                                                userToBeDeleted.Username, e),
                    AccountName = userToBeDeleted.Username
                });
            }
        }
        public async Task <FeatureFlagSaveResult> TrySaveAsync(FeatureFlags flags, string contentId)
        {
            var result = await TrySaveInternalAsync(flags, contentId);

            await _auditing.SaveAuditRecordAsync(
                new FeatureFlagsAuditRecord(
                    AuditedFeatureFlagsAction.Update,
                    flags,
                    contentId,
                    result));

            return(result);
        }
Ejemplo n.º 13
0
        public async Task <Certificate> AddCertificateAsync(HttpPostedFileBase file)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            _certificateValidator.Validate(file);

            using (var certificateFile = CertificateFile.Create(file.InputStream))
            {
                var certificate = GetCertificate(certificateFile.Sha256Thumbprint);

                if (certificate == null)
                {
                    await SaveToFileStorageAsync(certificateFile);

                    certificate = new Certificate()
                    {
#pragma warning disable CS0618 // Only set the SHA1 thumbprint, for backwards compatibility. Never read it.
                        Sha1Thumbprint = certificateFile.Sha1Thumbprint,
#pragma warning restore CS0618
                        Thumbprint       = certificateFile.Sha256Thumbprint,
                        UserCertificates = new List <UserCertificate>()
                    };

                    _certificateRepository.InsertOnCommit(certificate);

                    await _certificateRepository.CommitChangesAsync();

                    await _auditingService.SaveAuditRecordAsync(
                        new CertificateAuditRecord(AuditedCertificateAction.Add, certificate.Thumbprint));

                    _telemetryService.TrackCertificateAdded(certificateFile.Sha256Thumbprint);
                }

                return(certificate);
            }
        }
Ejemplo n.º 14
0
        public async Task <Certificate> AddCertificateAsync(HttpPostedFileBase file)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            _certificateValidator.Validate(file);

            using (var certificateFile = CertificateFile.Create(file.InputStream))
            {
                var certificate = GetCertificate(certificateFile.Sha256Thumbprint);

                if (certificate == null)
                {
                    await SaveToFileStorageAsync(certificateFile);

                    certificate = new Certificate()
                    {
                        Sha1Thumbprint   = certificateFile.Sha1Thumbprint,
                        Thumbprint       = certificateFile.Sha256Thumbprint,
                        UserCertificates = new List <UserCertificate>()
                    };

                    _certificateRepository.InsertOnCommit(certificate);

                    await _certificateRepository.CommitChangesAsync();

                    await _auditingService.SaveAuditRecordAsync(
                        new CertificateAuditRecord(AuditedCertificateAction.Add, certificate.Thumbprint));

                    _telemetryService.TrackCertificateAdded(certificateFile.Sha256Thumbprint);
                }

                return(certificate);
            }
        }
Ejemplo n.º 15
0
        public async Task <bool> TryAddDeleteSupportRequestAsync(User user)
        {
            var requestSent = await AddNewSupportRequestAsync(
                Strings.AccountDelete_SupportRequestTitle,
                Strings.AccountDelete_SupportRequestTitle,
                user.EmailAddress,
                "The user requested to have the account deleted.",
                user) != null;

            var status = requestSent ? DeleteAccountAuditRecord.ActionStatus.Success : DeleteAccountAuditRecord.ActionStatus.Failure;
            await _auditingService.SaveAuditRecordAsync(new DeleteAccountAuditRecord(username : user.Username,
                                                                                     status : status,
                                                                                     action : AuditedDeleteAccountAction.RequestAccountDeletion));

            return(requestSent);
        }
Ejemplo n.º 16
0
        public virtual async Task <JsonResult> Deprecate(
            string id,
            IEnumerable <string> versions,
            bool isLegacy,
            bool hasCriticalBugs,
            bool isOther,
            string alternatePackageId,
            string alternatePackageVersion,
            string customMessage)
        {
            var status = PackageDeprecationStatus.NotDeprecated;

            if (isLegacy)
            {
                status |= PackageDeprecationStatus.Legacy;
            }

            if (hasCriticalBugs)
            {
                status |= PackageDeprecationStatus.CriticalBugs;
            }

            if (isOther)
            {
                if (string.IsNullOrWhiteSpace(customMessage))
                {
                    return(DeprecateErrorResponse(HttpStatusCode.BadRequest, Strings.DeprecatePackage_CustomMessageRequired));
                }

                status |= PackageDeprecationStatus.Other;
            }

            var currentUser = GetCurrentUser();

            if (!_featureFlagService.IsManageDeprecationEnabled(GetCurrentUser()))
            {
                return(DeprecateErrorResponse(HttpStatusCode.Forbidden, Strings.DeprecatePackage_Forbidden));
            }

            if (versions == null || !versions.Any())
            {
                return(DeprecateErrorResponse(HttpStatusCode.BadRequest, Strings.DeprecatePackage_NoVersions));
            }

            var packages     = _packageService.FindPackagesById(id, PackageDeprecationFieldsToInclude.DeprecationAndRelationships);
            var registration = packages.FirstOrDefault()?.PackageRegistration;

            if (registration == null)
            {
                // This should only happen if someone hacks the form or if the package is deleted while the user is filling out the form.
                return(DeprecateErrorResponse(
                           HttpStatusCode.NotFound,
                           string.Format(Strings.DeprecatePackage_MissingRegistration, id)));
            }

            if (ActionsRequiringPermissions.DeprecatePackage.CheckPermissionsOnBehalfOfAnyAccount(currentUser, registration) != PermissionsCheckResult.Allowed)
            {
                return(DeprecateErrorResponse(HttpStatusCode.Forbidden, Strings.DeprecatePackage_Forbidden));
            }

            if (registration.IsLocked)
            {
                return(DeprecateErrorResponse(
                           HttpStatusCode.Forbidden,
                           string.Format(Strings.DeprecatePackage_Locked, id)));
            }

            PackageRegistration alternatePackageRegistration = null;
            Package             alternatePackage             = null;

            if (!string.IsNullOrWhiteSpace(alternatePackageId))
            {
                if (!string.IsNullOrWhiteSpace(alternatePackageVersion))
                {
                    alternatePackage = _packageService.FindPackageByIdAndVersionStrict(alternatePackageId, alternatePackageVersion);
                    if (alternatePackage == null)
                    {
                        return(DeprecateErrorResponse(
                                   HttpStatusCode.NotFound,
                                   string.Format(Strings.DeprecatePackage_NoAlternatePackage, alternatePackageId, alternatePackageVersion)));
                    }
                }
                else
                {
                    alternatePackageRegistration = _packageService.FindPackageRegistrationById(alternatePackageId);
                    if (alternatePackageRegistration == null)
                    {
                        return(DeprecateErrorResponse(
                                   HttpStatusCode.NotFound,
                                   string.Format(Strings.DeprecatePackage_NoAlternatePackageRegistration, alternatePackageId)));
                    }
                }
            }

            var packagesToUpdate = new List <Package>();

            foreach (var version in versions)
            {
                var normalizedVersion = NuGetVersionFormatter.Normalize(version);
                var package           = packages.SingleOrDefault(v => v.NormalizedVersion == normalizedVersion);
                if (package == null)
                {
                    // This should only happen if someone hacks the form or if a version of the package is deleted while the user is filling out the form.
                    return(DeprecateErrorResponse(
                               HttpStatusCode.NotFound,
                               string.Format(Strings.DeprecatePackage_MissingVersion, id)));
                }
                else
                {
                    packagesToUpdate.Add(package);
                }
            }

            await _deprecationService.UpdateDeprecation(
                packagesToUpdate,
                status,
                alternatePackageRegistration,
                alternatePackage,
                customMessage,
                currentUser);

            foreach (var packageToUpdate in packagesToUpdate)
            {
                await _auditingService.SaveAuditRecordAsync(
                    new PackageAuditRecord(
                        packageToUpdate,
                        status == PackageDeprecationStatus.NotDeprecated ? AuditedPackageAction.Undeprecate : AuditedPackageAction.Deprecate,
                        status == PackageDeprecationStatus.NotDeprecated ? PackageUndeprecatedVia.Web : PackageDeprecatedVia.Web));
            }

            return(Json(HttpStatusCode.OK));
        }
        /// <summary>
        /// Will clean-up the data related with an user account.
        /// The result will be:
        /// 1. The user will be removed as owner from its owned packages.
        /// 2. Any of the packages that become orphaned as its result will be unlisted if the unlistOrphanPackages is set to true.
        /// 3. Any owned namespaces will be released.
        /// 4. The user credentials will be cleaned.
        /// 5. The user data will be cleaned.
        /// </summary>
        /// <param name="userToBeDeleted">The user to be deleted.</param>
        /// <param name="admin">The admin that will perform the delete action.</param>
        /// <param name="signature">The admin signature.</param>
        /// <param name="unlistOrphanPackages">If the orphaned packages will unlisted.</param>
        /// <param name="commitAsTransaction">If the data will be persisted as a transaction.</param>
        /// <returns></returns>
        public async Task <DeleteUserAccountStatus> DeleteGalleryUserAccountAsync(User userToBeDeleted, User admin, string signature, bool unlistOrphanPackages, bool commitAsTransaction)
        {
            if (userToBeDeleted == null)
            {
                throw new ArgumentNullException(nameof(userToBeDeleted));
            }
            if (admin == null)
            {
                throw new ArgumentNullException(nameof(admin));
            }

            if (userToBeDeleted.IsDeleted)
            {
                return(new DeleteUserAccountStatus()
                {
                    Success = false,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_AccountAlreadyDeleted,
                                                userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }

            // The deletion of Organization and Organization member accounts is disabled for now.
            if (userToBeDeleted is Organization)
            {
                return(new DeleteUserAccountStatus()
                {
                    Success = false,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_OrganizationDeleteNotImplemented,
                                                userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }
            else if (userToBeDeleted.Organizations.Any())
            {
                return(new DeleteUserAccountStatus()
                {
                    Success = false,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_OrganizationMemberDeleteNotImplemented,
                                                userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }

            try
            {
                // The support requests db and gallery db are different.
                // TransactionScope can be used for doing transaction actions across db on the same server but not on different servers.
                // The below code will clean first the suppport requests and after the gallery data.
                // The order is important in order to allow the admin the oportunity to execute this step again.
                await RemoveSupportRequests(userToBeDeleted);

                if (commitAsTransaction)
                {
                    using (var strategy = new SuspendDbExecutionStrategy())
                        using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                        {
                            await DeleteGalleryUserAccountImplAsync(userToBeDeleted, admin, signature, unlistOrphanPackages);

                            transaction.Commit();
                        }
                }
                else
                {
                    await DeleteGalleryUserAccountImplAsync(userToBeDeleted, admin, signature, unlistOrphanPackages);
                }
                await _auditingService.SaveAuditRecordAsync(new DeleteAccountAuditRecord(username : userToBeDeleted.Username,
                                                                                         status : DeleteAccountAuditRecord.ActionStatus.Success,
                                                                                         action : AuditedDeleteAccountAction.DeleteAccount,
                                                                                         adminUsername : admin.Username));

                return(new DeleteUserAccountStatus()
                {
                    Success = true,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_Success,
                                                userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }
            catch (Exception e)
            {
                QuietLog.LogHandledException(e);
                return(new DeleteUserAccountStatus()
                {
                    Success = true,
                    Description = string.Format(CultureInfo.CurrentCulture,
                                                Strings.AccountDelete_Fail,
                                                userToBeDeleted.Username, e),
                    AccountName = userToBeDeleted.Username
                });
            }
        }
        public async Task UpdateDeprecation(
            IReadOnlyList <Package> packages,
            PackageDeprecationStatus status,
            PackageRegistration alternatePackageRegistration,
            Package alternatePackage,
            string customMessage,
            User user)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            if (packages == null || !packages.Any())
            {
                throw new ArgumentException(nameof(packages));
            }

            var registration = packages.First().PackageRegistration;

            if (packages.Select(p => p.PackageRegistrationKey).Distinct().Count() > 1)
            {
                throw new ArgumentException("All packages to deprecate must have the same ID.", nameof(packages));
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    var shouldDelete = status == PackageDeprecationStatus.NotDeprecated;
                    var deprecations = new List <PackageDeprecation>();
                    foreach (var package in packages)
                    {
                        var deprecation = package.Deprecations.SingleOrDefault();
                        if (shouldDelete)
                        {
                            if (deprecation != null)
                            {
                                package.Deprecations.Remove(deprecation);
                                deprecations.Add(deprecation);
                            }
                        }
                        else
                        {
                            if (deprecation == null)
                            {
                                deprecation = new PackageDeprecation
                                {
                                    Package = package
                                };

                                package.Deprecations.Add(deprecation);
                                deprecations.Add(deprecation);
                            }

                            deprecation.Status           = status;
                            deprecation.DeprecatedByUser = user;

                            deprecation.AlternatePackageRegistration = alternatePackageRegistration;
                            deprecation.AlternatePackage             = alternatePackage;

                            deprecation.CustomMessage = customMessage;
                        }
                    }

                    if (shouldDelete)
                    {
                        _entitiesContext.Deprecations.RemoveRange(deprecations);
                    }
                    else
                    {
                        _entitiesContext.Deprecations.AddRange(deprecations);
                    }

                    await _entitiesContext.SaveChangesAsync();

                    await _packageUpdateService.UpdatePackagesAsync(packages);

                    transaction.Commit();

                    _telemetryService.TrackPackageDeprecate(
                        packages,
                        status,
                        alternatePackageRegistration,
                        alternatePackage,
                        !string.IsNullOrWhiteSpace(customMessage));

                    foreach (var package in packages)
                    {
                        await _auditingService.SaveAuditRecordAsync(
                            new PackageAuditRecord(
                                package,
                                status == PackageDeprecationStatus.NotDeprecated ? AuditedPackageAction.Undeprecate : AuditedPackageAction.Deprecate,
                                status == PackageDeprecationStatus.NotDeprecated ? PackageUndeprecatedVia.Web : PackageDeprecatedVia.Web));
                    }
                }
        }