private async Task RemoveMemberships(User user, User requestingUser, AccountDeletionOrphanPackagePolicy orphanPackagePolicy)
        {
            foreach (var membership in user.Organizations.ToArray())
            {
                var organization  = membership.Organization;
                var members       = organization.Members.ToList();
                var collaborators = members.Where(m => !m.IsAdmin).ToList();
                var memberCount   = members.Count();
                user.Organizations.Remove(membership);

                if (memberCount < 2)
                {
                    // The user we are deleting is the only member of the organization.
                    // We should delete the entire organization.
                    await DeleteAccountImplAsync(organization, requestingUser, orphanPackagePolicy);
                }
                else if (memberCount - 1 <= collaborators.Count())
                {
                    // All other members of this organization are collaborators, so we should promote them to administrators.
                    foreach (var collaborator in collaborators)
                    {
                        collaborator.IsAdmin = true;
                    }
                }
            }

            foreach (var membershipRequest in user.OrganizationRequests.ToArray())
            {
                user.OrganizationRequests.Remove(membershipRequest);
            }

            foreach (var transformationRequest in user.OrganizationMigrationRequests.ToArray())
            {
                user.OrganizationMigrationRequests.Remove(transformationRequest);
                transformationRequest.NewOrganization.OrganizationMigrationRequest = null;
            }

            var migrationRequest = user.OrganizationMigrationRequest;

            user.OrganizationMigrationRequest = null;
            if (migrationRequest != null)
            {
                migrationRequest.AdminUser.OrganizationMigrationRequests.Remove(migrationRequest);
            }

            await _entitiesContext.SaveChangesAsync();
        }
        private async Task RemoveMemberships(User user, User requestingUser, AccountDeletionOrphanPackagePolicy orphanPackagePolicy)
        {
            foreach (var membership in user.Organizations.ToList())
            {
                user.Organizations.Remove(membership);
                var organization = membership.Organization;
                var otherMembers = organization.Members
                                   .Where(m => !m.Member.MatchesUser(user));

                if (!otherMembers.Any())
                {
                    // The user we are deleting is the only member of the organization.
                    // We should delete the entire organization.
                    await DeleteAccountImplAsync(organization, requestingUser, orphanPackagePolicy, commitChanges : false);
                }
                else if (otherMembers.All(m => !m.IsAdmin))
                {
                    // All other members of this organization are collaborators, so we should promote them to administrators.
                    foreach (var collaborator in otherMembers)
                    {
                        collaborator.IsAdmin = true;
                    }
                }
            }

            foreach (var membershipRequest in user.OrganizationRequests.ToList())
            {
                user.OrganizationRequests.Remove(membershipRequest);
            }

            foreach (var transformationRequest in user.OrganizationMigrationRequests.ToList())
            {
                user.OrganizationMigrationRequests.Remove(transformationRequest);
                transformationRequest.NewOrganization.OrganizationMigrationRequest = null;
            }

            var migrationRequest = user.OrganizationMigrationRequest;

            user.OrganizationMigrationRequest = null;
            if (migrationRequest != null)
            {
                migrationRequest.AdminUser.OrganizationMigrationRequests.Remove(migrationRequest);
            }
        }
示例#3
0
        private async Task RemovePackageOwnership(User user, User requestingUser, AccountDeletionOrphanPackagePolicy orphanPackagePolicy)
        {
            foreach (var package in GetPackagesOwnedByUser(user))
            {
                if (_packageService.WillPackageBeOrphanedIfOwnerRemoved(package.PackageRegistration, user))
                {
                    if (orphanPackagePolicy == AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans)
                    {
                        throw new InvalidOperationException($"Deleting user '{user.Username}' will make package '{package.PackageRegistration.Id}' an orphan, but no orphans were expected.");
                    }
                    else if (orphanPackagePolicy == AccountDeletionOrphanPackagePolicy.UnlistOrphans)
                    {
                        await _packageService.MarkPackageUnlistedAsync(package, commitChanges : true);
                    }
                }

                await _packageOwnershipManagementService.RemovePackageOwnerAsync(package.PackageRegistration, requestingUser, user, commitAsTransaction : false);
            }
        }
        private async Task RemovePackageOwnership(User user, User requestingUser, AccountDeletionOrphanPackagePolicy orphanPackagePolicy)
        {
            foreach (var package in GetPackagesOwnedByUser(user))
            {
                var owners = user is Organization ? package.PackageRegistration.Owners : _packageService.GetPackageUserAccountOwners(package);
                if (owners.Count() <= 1)
                {
                    // Package will be orphaned by removing ownership.
                    if (orphanPackagePolicy == AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans)
                    {
                        throw new InvalidOperationException($"Deleting user '{user.Username}' will make package '{package.PackageRegistration.Id}' an orphan, but no orphans were expected.");
                    }
                    else if (orphanPackagePolicy == AccountDeletionOrphanPackagePolicy.UnlistOrphans)
                    {
                        await _packageService.MarkPackageUnlistedAsync(package, commitChanges : true);
                    }
                }

                await _packageOwnershipManagementService.RemovePackageOwnerAsync(package.PackageRegistration, requestingUser, user, commitAsTransaction : false);
            }
        }
示例#5
0
        public async Task <DeleteUserAccountStatus> DeleteAccountAsync(User userToBeDeleted,
                                                                       User userToExecuteTheDelete,
                                                                       bool commitAsTransaction,
                                                                       AccountDeletionOrphanPackagePolicy orphanPackagePolicy = AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans)
        {
            if (userToBeDeleted == null)
            {
                throw new ArgumentNullException(nameof(userToBeDeleted));
            }

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

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

            var deleteUserAccountStatus = await RunAccountDeletionTask(
                () => DeleteAccountImplAsync(
                    userToBeDeleted,
                    userToExecuteTheDelete,
                    orphanPackagePolicy),
                userToBeDeleted,
                userToExecuteTheDelete,
                commitAsTransaction);

            _telemetryService.TrackAccountDeletionCompleted(userToBeDeleted, userToExecuteTheDelete, deleteUserAccountStatus.Success);
            return(deleteUserAccountStatus);
        }
示例#6
0
        private async Task DeleteAccountImplAsync(User userToBeDeleted, User userToExecuteTheDelete, AccountDeletionOrphanPackagePolicy orphanPackagePolicy)
        {
            await RemoveReservedNamespaces(userToBeDeleted);
            await RemovePackageOwnership(userToBeDeleted, userToExecuteTheDelete, orphanPackagePolicy);
            await RemoveMemberships(userToBeDeleted, userToExecuteTheDelete, orphanPackagePolicy);
            await RemoveSecurityPolicies(userToBeDeleted);
            await RemoveUserCredentials(userToBeDeleted);
            await RemovePackageOwnershipRequests(userToBeDeleted);

            var organizationToBeDeleted = userToBeDeleted as Organization;

            if (organizationToBeDeleted != null)
            {
                await RemoveMembers(organizationToBeDeleted);
            }

            if (!userToBeDeleted.Confirmed)
            {
                // Unconfirmed users should be hard-deleted.
                // Another account with the same username can be created.
                await RemoveUser(userToBeDeleted);
            }
            else
            {
                // Confirmed users should be soft-deleted.
                // Another account with the same username cannot be created.
                await RemoveUserDataInUserTable(userToBeDeleted);
                await InsertDeleteAccount(
                    userToBeDeleted,
                    userToExecuteTheDelete);
            }
        }
        private async Task DeleteAccountImplAsync(User userToBeDeleted, User userToExecuteTheDelete, AccountDeletionOrphanPackagePolicy orphanPackagePolicy, bool commitChanges = true)
        {
            await RemoveReservedNamespaces(userToBeDeleted);
            await RemovePackageOwnership(userToBeDeleted, userToExecuteTheDelete, orphanPackagePolicy);
            await RemoveMemberships(userToBeDeleted, userToExecuteTheDelete, orphanPackagePolicy);
            await RemoveSecurityPolicies(userToBeDeleted);
            await RemoveUserCredentials(userToBeDeleted);
            await RemovePackageOwnershipRequests(userToBeDeleted);

            ResetPackagesAndAccountsDeletedBy(userToBeDeleted);

            RemovePackagePushedBy(userToBeDeleted);
            RemovePackageDeprecatedBy(userToBeDeleted);

            var organizationToBeDeleted = userToBeDeleted as Organization;

            if (organizationToBeDeleted != null)
            {
                RemoveMembers(organizationToBeDeleted);
            }

            if (!userToBeDeleted.Confirmed)
            {
                // Unconfirmed users should be hard-deleted.
                // Another account with the same username can be created.
                RemoveUser(userToBeDeleted);
            }
            else
            {
                // Confirmed users should be soft-deleted.
                // Another account with the same username cannot be created.
                RemoveUserDataInUserTable(userToBeDeleted);
                InsertDeleteAccount(
                    userToBeDeleted,
                    userToExecuteTheDelete);
            }

            if (commitChanges)
            {
                await _entitiesContext.SaveChangesAsync();
            }
        }
            public async Task DeleteOrganization(bool isPackageOrphaned, AccountDeletionOrphanPackagePolicy orphanPolicy)
            {
                // Arrange
                var member = new User("testUser")
                {
                    Key = Key++
                };
                var organization = new Organization("testOrganization")
                {
                    Key          = Key++,
                    EmailAddress = "*****@*****.**"
                };

                var membership = new Membership()
                {
                    Organization = organization, Member = member
                };

                member.Organizations.Add(membership);
                organization.Members.Add(membership);

                var requestedMember = new User("testRequestedMember")
                {
                    Key = Key++
                };
                var memberRequest = new MembershipRequest()
                {
                    Organization = organization, NewMember = requestedMember
                };

                requestedMember.OrganizationRequests.Add(memberRequest);
                organization.MemberRequests.Add(memberRequest);

                PackageRegistration registration = new PackageRegistration();

                registration.Owners.Add(organization);

                Package p = new Package()
                {
                    Description = "TestPackage",
                    Key         = 1
                };

                p.PackageRegistration = registration;
                registration.Packages.Add(p);

                var testableService      = new DeleteAccountTestService(organization, registration);
                var deleteAccountService = testableService.GetDeleteAccountService(isPackageOrphaned);

                // Act
                var status = await deleteAccountService.
                             DeleteAccountAsync(
                    organization,
                    member,
                    commitAsTransaction : false,
                    orphanPackagePolicy : orphanPolicy);

                // Assert
                if (orphanPolicy == AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans && isPackageOrphaned)
                {
                    Assert.False(status.Success);
                    Assert.Equal(organization.Confirmed, organization.EmailAddress != null);
                    Assert.True(registration.Owners.Any(o => o.MatchesUser(organization)));
                    Assert.NotEmpty(organization.SecurityPolicies);
                    Assert.Empty(testableService.DeletedAccounts);
                    Assert.NotEmpty(testableService.PackageOwnerRequests);
                    Assert.Empty(testableService.AuditService.Records);
                    Assert.False(testableService.HasDeletedOwnerScope);
                    Assert.Empty(testableService.AuditService.Records);
                }
                else
                {
                    Assert.True(status.Success);
                    Assert.Null(organization.EmailAddress);
                    Assert.Equal(
                        orphanPolicy == AccountDeletionOrphanPackagePolicy.UnlistOrphans && isPackageOrphaned,
                        !registration.Packages.Single().Listed);
                    Assert.False(registration.Owners.Any(o => o.MatchesUser(organization)));
                    Assert.Empty(organization.SecurityPolicies);
                    Assert.Single(testableService.DeletedAccounts);
                    Assert.Empty(testableService.PackageOwnerRequests);
                    Assert.Single(testableService.AuditService.Records);
                    Assert.True(testableService.HasDeletedOwnerScope);

                    var deleteRecord = testableService.AuditService.Records[0] as DeleteAccountAuditRecord;
                    Assert.True(deleteRecord != null);
                }

                // Reserved namespaces and support requests are deleted before the request fails due to orphaned packages.
                // Because we are not committing as a transaction in these tests, they remain deleted.
                // In production, they would not be deleted because the transaction they were deleted in would fail.
                Assert.Empty(organization.ReservedNamespaces);
                Assert.Single(testableService.SupportRequests);
            }
            public async Task DeleteHappyUser(bool isPackageOrphaned, AccountDeletionOrphanPackagePolicy orphanPolicy)
            {
                // Arrange
                PackageRegistration registration = null;
                var testUser = CreateTestUser(ref registration);
                var testUserOrganizations = testUser.Organizations.ToList();
                var testableService       = new DeleteAccountTestService(testUser, registration);
                var deleteAccountService  = testableService.GetDeleteAccountService(isPackageOrphaned);

                // Act
                await deleteAccountService.
                DeleteAccountAsync(userToBeDeleted : testUser,
                                   userToExecuteTheDelete : testUser,
                                   commitAsTransaction : false,
                                   orphanPackagePolicy : orphanPolicy);


                if (orphanPolicy == AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans && isPackageOrphaned)
                {
                    Assert.True(registration.Owners.Any(o => o.MatchesUser(testUser)));
                    Assert.NotEmpty(testUser.SecurityPolicies);
                    Assert.True(registration.Packages.Single().Listed);
                    Assert.NotNull(testUser.EmailAddress);
                    Assert.Empty(testableService.DeletedAccounts);
                    Assert.NotEmpty(testableService.PackageOwnerRequests);
                    Assert.False(testableService.HasDeletedOwnerScope);
                    Assert.Empty(testableService.AuditService.Records);
                    Assert.NotNull(testUser.OrganizationMigrationRequest);
                    Assert.NotEmpty(testUser.OrganizationMigrationRequests);
                    Assert.NotEmpty(testUser.OrganizationRequests);
                    Assert.NotEmpty(testUser.Organizations);
                }
                else
                {
                    Assert.False(registration.Owners.Any(o => o.MatchesUser(testUser)));
                    Assert.Empty(testUser.SecurityPolicies);
                    Assert.Equal(
                        orphanPolicy == AccountDeletionOrphanPackagePolicy.UnlistOrphans && isPackageOrphaned,
                        !registration.Packages.Single().Listed);
                    Assert.Null(testUser.EmailAddress);
                    Assert.Single(testableService.DeletedAccounts);
                    Assert.Empty(testableService.PackageOwnerRequests);
                    Assert.True(testableService.HasDeletedOwnerScope);
                    Assert.Single(testableService.AuditService.Records);
                    Assert.Null(testUser.OrganizationMigrationRequest);
                    Assert.Empty(testUser.OrganizationMigrationRequests);
                    Assert.Empty(testUser.OrganizationRequests);

                    Assert.Empty(testUser.Organizations);
                    foreach (var testUserOrganization in testUserOrganizations)
                    {
                        var notDeletedMembers = testUserOrganization.Organization.Members.Where(m => m.Member != testUser);
                        if (notDeletedMembers.Any())
                        {
                            // If an organization that the deleted user was a part of had other members, it should have at least one admin.
                            Assert.Contains(notDeletedMembers, m => m.IsAdmin);
                        }
                        else
                        {
                            // If an organization that the deleted user was a part of had no other members, it should have been deleted.
                            Assert.Contains(testUserOrganization.Organization, testableService.DeletedUsers);
                        }
                    }

                    var deleteRecord = testableService.AuditService.Records[0] as DeleteAccountAuditRecord;
                    Assert.True(deleteRecord != null);
                }

                // Reserved namespaces and support requests are deleted before the request fails due to orphaned packages.
                // Because we are not committing as a transaction in these tests, they remain deleted.
                // In production, they would not be deleted because the transaction they were deleted in would fail.
                Assert.Single(testableService.SupportRequests);
                Assert.Empty(testUser.ReservedNamespaces);
            }
 public Task <DeleteAccountStatus> DeleteAccountAsync(User userToBeDeleted, User userToExecuteTheDelete, AccountDeletionOrphanPackagePolicy orphanPackagePolicy = AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans)
 {
     return(Task.FromResult(new DeleteAccountStatus()
     {
         Success = true,
     }));
 }
示例#11
0
        public async Task <DeleteAccountStatus> DeleteAccountAsync(User userToBeDeleted, User userToExecuteTheDelete, AccountDeletionOrphanPackagePolicy orphanPackagePolicy = AccountDeletionOrphanPackagePolicy.DoNotAllowOrphans)
        {
            if (userToBeDeleted == null)
            {
                throw new ArgumentNullException(nameof(userToBeDeleted));
            }

            var result = new DeleteAccountStatus()
            {
                AccountName = userToBeDeleted.Username
            };

            var isSupportRequestCreated = await _supportRequestService.TryAddDeleteSupportRequestAsync(userToBeDeleted);

            if (!isSupportRequestCreated)
            {
                result.Success     = false;
                result.Description = ServicesStrings.AccountDelete_CreateSupportRequestFails;
                return(result);
            }

            var sourceName = GalleryUserAccountDeleteMessageSourceName;

            if (userToExecuteTheDelete.IsAdministrator)
            {
                sourceName = GalleryAdminAccountDeleteMessageSourceName;
            }

            var messageData = new AccountDeleteMessage(userToBeDeleted.Username, source: sourceName);

            var message = _serializer.Serialize(messageData);

            try
            {
                await _topicClient.SendAsync(message);

                // if SendAsync doesn't throw, as far as we can tell, the message went through.
                result.Description = string.Format(CultureInfo.CurrentCulture,
                                                   ServicesStrings.AsyncAccountDelete_Success,
                                                   userToBeDeleted.Username);
                result.Success = true;
            }
            catch (Exception ex)
            {
                // See https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messaging-exceptions for a list of possible exceptions
                _logger.LogError(0, ex, "Failed to enqueue to AccountDeleter.");
                result.Success     = false;
                result.Description = ServicesStrings.AsyncAccountDelete_Fail;
            }

            return(result);
        }