コード例 #1
0
        public async Task DeleteOwnerFromReservedNamespaceAsync(string prefix, string username, bool commitChanges = true)
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidNamespace);
            }

            if (string.IsNullOrWhiteSpace(username))
            {
                throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidUsername);
            }
            var namespaceToModify = FindReservedNamespaceForPrefix(prefix)
                                    ?? throw new InvalidOperationException(string.Format(
                                                                               CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_NamespaceNotFound, prefix));
            List <PackageRegistration> packageRegistrationsToMarkUnverified;

            if (commitChanges)
            {
                using (var strategy = new SuspendDbExecutionStrategy())
                    using (var transaction = EntitiesContext.GetDatabase().BeginTransaction())
                    {
                        packageRegistrationsToMarkUnverified = await DeleteOwnerFromReservedNamespaceImplAsync(prefix, username, namespaceToModify);

                        transaction.Commit();
                    }
            }
            else
            {
                packageRegistrationsToMarkUnverified = await DeleteOwnerFromReservedNamespaceImplAsync(prefix, username, namespaceToModify, commitChanges : false);
            }

            await AuditingService.SaveAuditRecordAsync(
                new ReservedNamespaceAuditRecord(namespaceToModify, AuditedReservedNamespaceAction.RemoveOwner, username, packageRegistrationsToMarkUnverified));
        }
コード例 #2
0
        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));
        }
コード例 #3
0
        public async Task DeleteReservedNamespaceAsync(string existingNamespace)
        {
            if (string.IsNullOrWhiteSpace(existingNamespace))
            {
                throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidNamespace);
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = EntitiesContext.GetDatabase().BeginTransaction())
                {
                    var namespaceToDelete = FindReservedNamespaceForPrefix(existingNamespace)
                                            ?? throw new InvalidOperationException(string.Format(
                                                                                       CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_NamespaceNotFound, existingNamespace));

                    // Delete verified flags on corresponding packages for this prefix if
                    // it is the only prefix matching the package registration.
                    var packageRegistrationsToMarkUnverified = namespaceToDelete
                                                               .PackageRegistrations
                                                               .Where(pr => pr.ReservedNamespaces.Count() == 1)
                                                               .ToList();

                    if (packageRegistrationsToMarkUnverified.Any())
                    {
                        await PackageService.UpdatePackageVerifiedStatusAsync(packageRegistrationsToMarkUnverified, isVerified : false);
                    }

                    ReservedNamespaceRepository.DeleteOnCommit(namespaceToDelete);
                    await ReservedNamespaceRepository.CommitChangesAsync();

                    transaction.Commit();

                    await AuditingService.SaveAuditRecordAsync(
                        new ReservedNamespaceAuditRecord(namespaceToDelete, AuditedReservedNamespaceAction.UnreserveNamespace));
                }
        }
コード例 #4
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();
        }
コード例 #5
0
        public async Task HardDeletePackagesAsync(IEnumerable <Package> packages, User deletedBy, string reason, string signature, bool deleteEmptyPackageRegistration)
        {
            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);

                    // Remove the package and related entities from the database
                    foreach (var package in packages)
                    {
                        UnlinkPackageDeprecations(package);

                        await ExecuteSqlCommandAsync(_entitiesContext.GetDatabase(),
                                                     "DELETE pa FROM PackageAuthors pa JOIN Packages p ON p.[Key] = pa.PackageKey WHERE p.[Key] = @key",
                                                     new SqlParameter("@key", package.Key));
                        await ExecuteSqlCommandAsync(_entitiesContext.GetDatabase(),
                                                     "DELETE pd FROM PackageDependencies pd JOIN Packages p ON p.[Key] = pd.PackageKey WHERE p.[Key] = @key",
                                                     new SqlParameter("@key", package.Key));
                        await ExecuteSqlCommandAsync(_entitiesContext.GetDatabase(),
                                                     "DELETE pf FROM PackageFrameworks pf JOIN Packages p ON p.[Key] = pf.Package_Key WHERE p.[Key] = @key",
                                                     new SqlParameter("@key", package.Key));

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

                        _telemetryService.TrackPackageDelete(package, isHardDelete: true);

                        package.PackageRegistration.Packages.Remove(package);
                        _packageRepository.DeleteOnCommit(package);
                    }

                    // Update latest versions
                    await UpdateIsLatestAsync(packageRegistrations);

                    // Commit changes to package repository
                    await _packageRepository.CommitChangesAsync();

                    // Remove package registrations that have no more packages?
                    if (deleteEmptyPackageRegistration)
                    {
                        await RemovePackageRegistrationsWithoutPackages(packageRegistrations);
                    }

                    // Commit transaction
                    transaction.Commit();
                }

            // Force refresh the index
            UpdateSearchIndex();
        }
コード例 #6
0
        public async Task DeleteOwnerFromReservedNamespaceAsync(string prefix, string username)
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                throw new ArgumentException(Strings.ReservedNamespace_InvalidNamespace);
            }

            if (string.IsNullOrWhiteSpace(username))
            {
                throw new ArgumentException(Strings.ReservedNamespace_InvalidUsername);
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = EntitiesContext.GetDatabase().BeginTransaction())
                {
                    var namespaceToModify = FindReservedNamespaceForPrefix(prefix)
                                            ?? throw new InvalidOperationException(string.Format(
                                                                                       CultureInfo.CurrentCulture, Strings.ReservedNamespace_NamespaceNotFound, prefix));

                    var userToRemove = UserService.FindByUsername(username)
                                       ?? throw new InvalidOperationException(string.Format(
                                                                                  CultureInfo.CurrentCulture, Strings.ReservedNamespace_UserNotFound, username));

                    if (!namespaceToModify.Owners.Contains(userToRemove))
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.ReservedNamespace_UserNotAnOwner, username));
                    }

                    var packagesOwnedByUserMatchingPrefix = namespaceToModify
                                                            .PackageRegistrations
                                                            .Where(pr => pr
                                                                   .Owners
                                                                   .Any(pro => pro.Username == userToRemove.Username))
                                                            .ToList();

                    // Remove verified mark for package registrations if the user to be removed is the only prefix owner
                    // for the given package registration.
                    var packageRegistrationsToMarkUnverified = packagesOwnedByUserMatchingPrefix
                                                               .Where(pr => pr.Owners.Intersect(namespaceToModify.Owners).Count() == 1)
                                                               .ToList();

                    if (packageRegistrationsToMarkUnverified.Any())
                    {
                        packageRegistrationsToMarkUnverified
                        .ForEach(pr => namespaceToModify.PackageRegistrations.Remove(pr));

                        await PackageService.UpdatePackageVerifiedStatusAsync(packageRegistrationsToMarkUnverified, isVerified : false);
                    }

                    namespaceToModify.Owners.Remove(userToRemove);
                    await ReservedNamespaceRepository.CommitChangesAsync();

                    transaction.Commit();
                }
        }
コード例 #7
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)
                    {
                        package.Listed           = false;
                        package.Deleted          = true;
                        package.PackageStatusKey = PackageStatus.Deleted;
                        packageDelete.Packages.Add(package);

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

                    _packageDeletesRepository.InsertOnCommit(packageDelete);

                    // Update latest versions
                    await UpdateIsLatestAsync(packageRegistrations);

                    // Commit changes
                    await _packageRepository.CommitChangesAsync();

                    await _packageDeletesRepository.CommitChangesAsync();

                    transaction.Commit();
                }


            // Force refresh the index
            UpdateSearchIndex();
        }
コード例 #8
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
                });
            }
        }
コード例 #9
0
        /// <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(Strings.AccountDelete_AccountAlreadyDeleted, userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }
            try
            {
                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);
                }
                return(new DeleteUserAccountStatus()
                {
                    Success = true,
                    Description = string.Format(Strings.AccountDelete_Success, userToBeDeleted.Username),
                    AccountName = userToBeDeleted.Username
                });
            }
            catch (Exception e)
            {
                QuietLog.LogHandledException(e);
                return(new DeleteUserAccountStatus()
                {
                    Success = true,
                    Description = string.Format(Strings.AccountDelete_Fail, userToBeDeleted.Username, e),
                    AccountName = userToBeDeleted.Username
                });
            }
        }
        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));
        }
        public async Task AddOwnerToReservedNamespaceAsync(string prefix, string username)
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                throw new ArgumentException(Strings.ReservedNamespace_InvalidNamespace);
            }

            if (string.IsNullOrWhiteSpace(username))
            {
                throw new ArgumentException(Strings.ReservedNamespace_InvalidUsername);
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = EntitiesContext.GetDatabase().BeginTransaction())
                {
                    var namespaceToModify = FindReservedNamespaceForPrefix(prefix)
                                            ?? throw new InvalidOperationException(string.Format(
                                                                                       CultureInfo.CurrentCulture, Strings.ReservedNamespace_NamespaceNotFound, prefix));

                    var userToAdd = UserService.FindByUsername(username)
                                    ?? throw new InvalidOperationException(string.Format(
                                                                               CultureInfo.CurrentCulture, Strings.ReservedNamespace_UserNotFound, username));

                    if (namespaceToModify.Owners.Contains(userToAdd))
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.ReservedNamespace_UserAlreadyOwner, username));
                    }

                    // Mark all packages owned by this user that start with the given namespace as verified.
                    var allPackageRegistrationsForUser        = PackageService.FindPackageRegistrationsByOwner(userToAdd);
                    var packageRegistrationsMatchingNamespace = allPackageRegistrationsForUser
                                                                .Where(pr => pr.Id.StartsWith(namespaceToModify.Value, StringComparison.OrdinalIgnoreCase))
                                                                .ToList();

                    if (packageRegistrationsMatchingNamespace.Any())
                    {
                        packageRegistrationsMatchingNamespace
                        .ForEach(pr => namespaceToModify.PackageRegistrations.Add(pr));

                        await PackageService.UpdatePackageVerifiedStatusAsync(packageRegistrationsMatchingNamespace.AsReadOnly(), isVerified : true);
                    }

                    namespaceToModify.Owners.Add(userToAdd);
                    await ReservedNamespaceRepository.CommitChangesAsync();

                    transaction.Commit();
                }
        }
        public async Task RemovePackageOwnerAsync(PackageRegistration packageRegistration, User requestingOwner, User ownerToBeRemoved, bool commitAsTransaction = true)
        {
            if (packageRegistration == null)
            {
                throw new ArgumentNullException(nameof(packageRegistration));
            }

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

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

            if (OwnerHasPermissionsToRemove(requestingOwner, ownerToBeRemoved, packageRegistration))
            {
                if (commitAsTransaction)
                {
                    using (var strategy = new SuspendDbExecutionStrategy())
                        using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                        {
                            await RemovePackageOwnerImplAsync(packageRegistration, ownerToBeRemoved);

                            transaction.Commit();
                        }
                }
                else
                {
                    await RemovePackageOwnerImplAsync(packageRegistration, ownerToBeRemoved);
                }

                await _auditingService.SaveAuditRecordAsync(
                    new PackageRegistrationAuditRecord(packageRegistration, AuditedPackageRegistrationAction.RemoveOwner, ownerToBeRemoved.Username));
            }
            else
            {
                throw new InvalidOperationException(string.Format(Strings.RemoveOwner_NotAllowed, requestingOwner.Username, ownerToBeRemoved.Username));
            }
        }
コード例 #13
0
        public async Task UpdateVulnerabilityAsync(PackageVulnerability vulnerability, bool withdrawn)
        {
            if (vulnerability == null)
            {
                throw new ArgumentNullException(nameof(vulnerability));
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    var packagesToUpdate = UpdateVulnerabilityInternal(vulnerability, withdrawn);
                    await _entitiesContext.SaveChangesAsync();

                    if (packagesToUpdate.Any())
                    {
                        await _packageUpdateService.UpdatePackagesAsync(packagesToUpdate.ToList());
                    }

                    transaction.Commit();
                }
        }
コード例 #14
0
        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));
                    }
                }
        }
コード例 #15
0
        public async Task AddOwnerToReservedNamespaceAsync(string prefix, string username)
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidNamespace);
            }

            if (string.IsNullOrWhiteSpace(username))
            {
                throw new ArgumentException(ServicesStrings.ReservedNamespace_InvalidUsername);
            }

            using (var strategy = new SuspendDbExecutionStrategy())
                using (var transaction = EntitiesContext.GetDatabase().BeginTransaction())
                {
                    var namespaceToModify = FindReservedNamespaceForPrefix(prefix)
                                            ?? throw new InvalidOperationException(string.Format(
                                                                                       CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_NamespaceNotFound, prefix));

                    var userToAdd = UserService.FindByUsername(username)
                                    ?? throw new InvalidOperationException(string.Format(
                                                                               CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_UserNotFound, username));

                    if (namespaceToModify.Owners.Contains(userToAdd))
                    {
                        throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ServicesStrings.ReservedNamespace_UserAlreadyOwner, username));
                    }

                    Expression <Func <PackageRegistration, bool> > predicate;
                    if (namespaceToModify.IsPrefix)
                    {
                        predicate = registration => registration.Id.StartsWith(namespaceToModify.Value);
                    }
                    else
                    {
                        predicate = registration => registration.Id.Equals(namespaceToModify.Value);
                    }

                    // Mark all packages owned by this user that start with the given namespace as verified.
                    var allPackageRegistrationsForUser = PackageService.FindPackageRegistrationsByOwner(userToAdd);

                    // We need 'AsQueryable' here because FindPackageRegistrationsByOwner returns an IEnumerable
                    // and to evaluate the predicate server side, the casting is essential.
                    var packageRegistrationsMatchingNamespace = allPackageRegistrationsForUser
                                                                .AsQueryable()
                                                                .Where(predicate)
                                                                .ToList();

                    if (packageRegistrationsMatchingNamespace.Any())
                    {
                        packageRegistrationsMatchingNamespace
                        .ForEach(pr => namespaceToModify.PackageRegistrations.Add(pr));

                        await PackageService.UpdatePackageVerifiedStatusAsync(packageRegistrationsMatchingNamespace.AsReadOnly(), isVerified : true);
                    }

                    namespaceToModify.Owners.Add(userToAdd);
                    await ReservedNamespaceRepository.CommitChangesAsync();

                    transaction.Commit();

                    await AuditingService.SaveAuditRecordAsync(
                        new ReservedNamespaceAuditRecord(namespaceToModify, AuditedReservedNamespaceAction.AddOwner, username, packageRegistrationsMatchingNamespace));
                }
        }
        public async Task RemovePackageOwnerAsync(PackageRegistration packageRegistration, User requestingOwner, User ownerToBeRemoved)
        {
            if (packageRegistration == null)
            {
                throw new ArgumentNullException(nameof(packageRegistration));
            }

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

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

            if (OwnerHasPermissionsToRemove(requestingOwner, ownerToBeRemoved, packageRegistration))
            {
                using (var strategy = new SuspendDbExecutionStrategy())
                    using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                    {
                        // 1. Remove this package registration from the namespaces owned by this user if he is the only package owner in the set of matching namespaces
                        // 2. Remove the IsVerified flag from package registration if all the matching namespaces are owned by this user alone (no other package owner owns a matching namespace for this PR)
                        var allMatchingNamespacesForRegistration = packageRegistration.ReservedNamespaces;
                        if (allMatchingNamespacesForRegistration.Any())
                        {
                            var allPackageOwners = packageRegistration.Owners;
                            var matchingNamespacesOwnedByUser = allMatchingNamespacesForRegistration
                                                                .Where(rn => rn.Owners.Any(o => o == ownerToBeRemoved));
                            var namespacesToModify = matchingNamespacesOwnedByUser
                                                     .Where(rn => rn.Owners.Intersect(allPackageOwners).Count() == 1)
                                                     .ToList();

                            if (namespacesToModify.Any())
                            {
                                // The package will lose its 'IsVerified' flag if the user is the only package owner who owns all the namespaces that match this registration
                                var shouldModifyIsVerified = allMatchingNamespacesForRegistration.Count() == namespacesToModify.Count();
                                if (shouldModifyIsVerified && packageRegistration.IsVerified)
                                {
                                    await _packageService.UpdatePackageVerifiedStatusAsync(new List <PackageRegistration> {
                                        packageRegistration
                                    }, isVerified : false);
                                }

                                namespacesToModify
                                .ForEach(rn => _reservedNamespaceService.RemovePackageRegistrationFromNamespace(rn.Value, packageRegistration));

                                await _entitiesContext.SaveChangesAsync();
                            }
                        }

                        // Remove the user from owners list of package registration
                        await _packageService.RemovePackageOwnerAsync(packageRegistration, ownerToBeRemoved);

                        transaction.Commit();
                    }

                await _auditingService.SaveAuditRecordAsync(
                    new PackageRegistrationAuditRecord(packageRegistration, AuditedPackageRegistrationAction.RemoveOwner, ownerToBeRemoved.Username));
            }
            else
            {
                throw new InvalidOperationException(string.Format(Strings.RemoveOwner_NotAllowed, requestingOwner.Username, ownerToBeRemoved.Username));
            }
        }
コード例 #17
0
        /// <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
                });
            }
        }