public async Task <OperationResult <IReadOnlyCollection <PermissionIdentityViewModel> > > GetGrantedPermissionsAsync(
            ulong userId,
            CancellationToken cancellationToken)
        {
            UsersLogMessages.GrantedPermissionIdentitiesFetching(_logger, userId);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            if (_authorizationConfigurationOptions.Value.AdminUserIds.Contains(userId))
            {
                UsersLogMessages.UserIsAdmin(_logger, userId);
                return((await _permissionsService.GetIdentitiesAsync(cancellationToken))
                       .ToSuccess());
            }

            var userExists = await _usersRepository.AnyAsync(
                userId : userId,
                cancellationToken : cancellationToken);

            if (!userExists)
            {
                UsersLogMessages.UserNotFound(_logger, userId);
                return(new DataNotFoundError($"User ID {userId}"));
            }

            UsersLogMessages.UserFound(_logger, userId);
            var identities = await _usersRepository.AsyncEnumerateGrantedPermissionIdentities(userId)
                             .ToArrayAsync(cancellationToken);

            UsersLogMessages.GrantedPermissionIdentitiesFetched(_logger, userId);

            return(identities
                   .ToSuccess <IReadOnlyCollection <PermissionIdentityViewModel> >());
        }
Exemple #2
0
        public async Task <OperationResult <long> > CreateAsync(
            RoleCreationModel creationModel,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            RolesLogMessages.RoleCreating(_logger, creationModel, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var nameValidationResult = await ValidateNameAsync(creationModel.Name, null, cancellationToken);

            if (nameValidationResult.IsFailure)
            {
                RolesLogMessages.RoleNameValidationFailed(_logger, creationModel.Name, nameValidationResult);
                return(nameValidationResult.Error);
            }
            RolesLogMessages.RoleNameValidationSucceeded(_logger, creationModel.Name);

            var grantedPermissionIdsValidationResult = await _permissionsService.ValidateIdsAsync(creationModel.GrantedPermissionIds, cancellationToken);

            if (grantedPermissionIdsValidationResult.IsFailure)
            {
                RolesLogMessages.PermissionIdsValidationFailed(_logger, creationModel.GrantedPermissionIds, grantedPermissionIdsValidationResult);
                return(grantedPermissionIdsValidationResult.Error);
            }
            RolesLogMessages.PermissionIdsValidationSucceeded(_logger, creationModel.GrantedPermissionIds);

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)RoleManagementAdministrationActionType.RoleCreated,
                _systemClock.UtcNow,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var roleId = await _rolesRepository.CreateAsync(
                creationModel.Name,
                actionId,
                cancellationToken);

            RolesLogMessages.RoleCreated(_logger, roleId);

            var mappingIds = await _rolesRepository.CreatePermissionMappingsAsync(
                roleId,
                creationModel.GrantedPermissionIds,
                actionId,
                cancellationToken);

            RolesLogMessages.RolePermissionMappingsCreated(_logger, roleId, mappingIds);

            _memoryCache.Remove(_getCurrentIdentitiesCacheKey);
            RolesLogMessages.RoleIdentitiesCacheCleared(_logger);

            transactionScope.Complete();
            TransactionsLogMessages.TransactionScopeCommitted(_logger);

            return(roleId.ToSuccess());
        }
Exemple #3
0
        public async Task <OperationResult> UpdateAsync(
            long guildId,
            long divisionId,
            CharacterGuildDivisionUpdateModel updateModel,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterGuildDivisionUpdating(_logger, guildId, divisionId, updateModel, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var guildIdValidationResult = await ValidateGuildIdAsync(guildId, cancellationToken);

            if (guildIdValidationResult.IsFailure)
            {
                CharactersLogMessages.CharacterGuildIdValidationFailed(_logger, guildId, guildIdValidationResult);
                return(guildIdValidationResult);
            }
            CharactersLogMessages.CharacterGuildIdValidationSucceeded(_logger, guildId);

            var nameValidationResult = await ValidateDivisionNameAsync(guildId, updateModel.Name, divisionId, cancellationToken);

            if (nameValidationResult.IsFailure)
            {
                CharactersLogMessages.CharacterGuildDivisionNameValidationFailed(_logger, updateModel.Name, nameValidationResult);
                return(nameValidationResult);
            }
            CharactersLogMessages.CharacterGuildDivisionNameValidationSucceeded(_logger, updateModel.Name);

            var now = _systemClock.UtcNow;

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)CharacterManagementAdministrationActionType.DivisionModified,
                now,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var updateResult = await _characterGuildDivisionsRepository.UpdateAsync(
                divisionId : divisionId,
                actionId : actionId,
                name : updateModel.Name,
                cancellationToken : cancellationToken);

            if (updateResult.IsFailure)
            {
                CharactersLogMessages.CharacterGuildDivisionUpdateFailed(_logger, guildId, divisionId, updateResult);
                return(updateResult);
            }
            CharactersLogMessages.CharacterGuildDivisionUpdated(_logger, guildId, divisionId);

            transactionScope.Complete();
            TransactionsLogMessages.TransactionScopeCommitted(_logger);

            return(OperationResult.Success);
        }
        public async Task <long> CreateAsync(
            string name,
            long actionId,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterGuildCreating(_logger, name, actionId);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var guild = new CharacterGuildEntity(
                id: default);
        public async Task <long> CreateAsync(
            string name,
            long actionId,
            CancellationToken cancellationToken)
        {
            RolesLogMessages.RoleCreating(_logger, name, actionId);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var role = new RoleEntity(
                id: default);
        protected override async Task OnStartingAsync(
            IServiceProvider serviceProvider,
            CancellationToken cancellationToken)
        {
            if (!_yastahAutoMigrationStartupAction.WhenDone.IsCompletedSuccessfully)
            {
                YastahDbContextLogMessages.ContextMigrationAwaiting(_logger);
            }
            await _yastahAutoMigrationStartupAction.WhenDone;

            CharactersLogMessages.CharacterLevelDefinitionsInitializing(_logger);

            var auditableActionsRepository = serviceProvider.GetRequiredService <IAuditableActionsRepository>();
            var characterLevelsRepository  = serviceProvider.GetRequiredService <ICharacterLevelsRepository>();

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var level1Exists = await characterLevelsRepository.AnyDefinitionsAsync(
                level :               1,
                experienceThreshold : 0,
                isDeleted :           false,
                cancellationToken :   cancellationToken);

            if (!level1Exists)
            {
                CharactersLogMessages.CharacterLevelsNotInitialized(_logger);

                var actionId = await auditableActionsRepository.CreateAsync(
                    (int)CharacterManagementAdministrationActionType.LevelDefinitionsInitialized,
                    _systemClock.UtcNow,
                    null,
                    cancellationToken);

                AuditingLogMessages.AuditingActionCreated(_logger, actionId);

                await characterLevelsRepository.MergeDefinitionAsync(
                    1,
                    0,
                    false,
                    actionId,
                    cancellationToken);

                CharactersLogMessages.CharacterLevelDefinition1Created(_logger);
            }

            TransactionsLogMessages.TransactionScopeCommitting(_logger);
            transactionScope.Complete();

            CharactersLogMessages.CharacterLevelDefinitionsInitialized(_logger);
        }
Exemple #7
0
        public async Task <OperationResult <long> > CreateAsync(
            long guildId,
            CharacterGuildDivisionCreationModel creationModel,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterGuildDivisionCreating(_logger, guildId, creationModel, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var guildIdValidationResult = await ValidateGuildIdAsync(guildId, cancellationToken);

            if (guildIdValidationResult.IsFailure)
            {
                CharactersLogMessages.CharacterGuildIdValidationFailed(_logger, guildId, guildIdValidationResult);
                return(guildIdValidationResult.Error);
            }
            CharactersLogMessages.CharacterGuildIdValidationSucceeded(_logger, guildId);

            var nameValidationResult = await ValidateDivisionNameAsync(guildId, creationModel.Name, null, cancellationToken);

            if (nameValidationResult.IsFailure)
            {
                CharactersLogMessages.CharacterGuildDivisionNameValidationFailed(_logger, creationModel.Name, nameValidationResult);
                return(nameValidationResult.Error);
            }
            CharactersLogMessages.CharacterGuildDivisionNameValidationSucceeded(_logger, creationModel.Name);

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)CharacterManagementAdministrationActionType.DivisionCreated,
                _systemClock.UtcNow,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var divisionId = await _characterGuildDivisionsRepository.CreateAsync(
                guildId,
                creationModel.Name,
                actionId,
                cancellationToken);

            CharactersLogMessages.CharacterGuildDivisionCreated(_logger, guildId, divisionId);

            transactionScope.Complete();
            TransactionsLogMessages.TransactionScopeCommitted(_logger);

            return(divisionId.ToSuccess());
        }
Exemple #8
0
        public async Task <OperationResult> DeleteAsync(
            long roleId,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            RolesLogMessages.RoleDeleting(_logger, roleId, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)RoleManagementAdministrationActionType.RoleDeleted,
                _systemClock.UtcNow,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var deleteResult = await _rolesRepository.UpdateAsync(
                roleId : roleId,
                actionId : actionId,
                isDeleted : true,
                cancellationToken : cancellationToken);

            if (deleteResult.IsSuccess)
            {
                RolesLogMessages.RoleDeleted(_logger, roleId, deleteResult.Value);

                _memoryCache.Remove(_getCurrentIdentitiesCacheKey);
                RolesLogMessages.RoleIdentitiesCacheCleared(_logger);

                RolesLogMessages.RoleDeletingNotificationPublishing(_logger, roleId);
                await _messenger.PublishNotificationAsync(
                    new RoleDeletingNotification(
                        roleId,
                        actionId),
                    cancellationToken);

                RolesLogMessages.RoleDeletingNotificationPublished(_logger, roleId);

                transactionScope.Complete();
                TransactionsLogMessages.TransactionScopeCommitted(_logger);
            }
            else
            {
                RolesLogMessages.RoleDeleteFailed(_logger, roleId, deleteResult);
            }

            return(deleteResult);
        }
Exemple #9
0
        public async Task <long> CreateAsync(
            ulong ownerId,
            string name,
            long divisionId,
            int experiencePoints,
            int goldAmount,
            int insanityValue,
            long actionId,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterCreating(_logger, ownerId, name, divisionId, experiencePoints, goldAmount, insanityValue, actionId);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var character = new CharacterEntity(
                id:         default,
        private async Task UpdateActiveTicketId(
            ulong userId,
            long actionId,
            CancellationToken cancellationToken)
        {
            AuthenticationLogMessages.AuthenticationTicketInvalidating(_logger, userId, actionId);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            AuthenticationLogMessages.AuthenticationTicketActiveIdFetching(_logger, userId);
            var activeTicketIdResult = await _authenticationTicketsRepository.ReadActiveIdAsync(userId, cancellationToken);

            if (activeTicketIdResult.IsSuccess)
            {
                AuthenticationLogMessages.AuthenticationTicketActiveIdFetched(_logger, activeTicketIdResult.Value);
                AuthenticationLogMessages.AuthenticationTicketDeleting(_logger, userId, activeTicketIdResult.Value);
                await _authenticationTicketsRepository.DeleteAsync(
                    activeTicketIdResult.Value,
                    actionId,
                    cancellationToken);

                AuthenticationLogMessages.AuthenticationTicketDeleted(_logger, userId, activeTicketIdResult.Value);
            }
            else
            {
                AuthenticationLogMessages.AuthenticationTicketActiveIdFetched(_logger, null);
            }

            AuthenticationLogMessages.AuthenticationTicketCreating(_logger, userId);
            var newTicketId = await _authenticationTicketsRepository.CreateAsync(
                userId,
                actionId,
                cancellationToken);

            AuthenticationLogMessages.AuthenticationTicketCreated(_logger, userId, newTicketId);

            _memoryCache.Set(MakeUserActiveTicketIdCacheKey(userId), newTicketId);

            TransactionsLogMessages.TransactionScopeCommitting(_logger);
            transactionScope.Complete();

            AuthenticationLogMessages.AuthenticationTicketInvalidated(_logger, userId, newTicketId);
        }
Exemple #11
0
        public async Task <OperationResult> MergeDefinitionAsync(
            int level,
            int experienceThreshold,
            bool isDeleted,
            long actionId,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterLevelDefinitionMerging(_logger, level, experienceThreshold, isDeleted, actionId);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var currentVersion = await _context.Set <CharacterLevelDefinitionVersionEntity>()
                                 .AsQueryable()
                                 .Where(x => x.Level == level)
                                 .Where(x => x.NextVersionId == null)
                                 .FirstOrDefaultAsync(cancellationToken);

            var newVersion = new CharacterLevelDefinitionVersionEntity(
                id:                     default,
        public async Task <OperationResult> DeleteAsync(
            long guildId,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterGuildDeleting(_logger, guildId, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)CharacterManagementAdministrationActionType.GuildDeleted,
                _systemClock.UtcNow,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var updateResult = await _characterGuildsRepository.UpdateAsync(
                guildId : guildId,
                actionId : actionId,
                isDeleted : true,
                cancellationToken : cancellationToken);

            if (updateResult.IsSuccess)
            {
                CharactersLogMessages.CharacterGuildDeleted(_logger, guildId);
                transactionScope.Complete();
                TransactionsLogMessages.TransactionScopeCommitted(_logger);
            }
            else
            {
                CharactersLogMessages.CharacterGuildDeleteFailed(_logger, guildId, updateResult);
            }

            return(updateResult);
        }
        public async Task <OperationResult> UpdateAsync(
            ulong userId,
            UserUpdateModel updateModel,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            UsersLogMessages.UserUpdating(_logger, userId, updateModel, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var permissionIds = Enumerable.Union(
                updateModel.GrantedPermissionIds,
                updateModel.DeniedPermissionIds)
                                .ToArray();

            UsersLogMessages.PermissionIdsValidating(_logger, permissionIds);
            var permissionIdsValidationResult = await _permissionsService.ValidateIdsAsync(
                permissionIds,
                cancellationToken);

            if (permissionIdsValidationResult.IsFailure)
            {
                UsersLogMessages.PermissionIdsValidationFailed(_logger, permissionIdsValidationResult);
                return(permissionIdsValidationResult);
            }
            UsersLogMessages.PermissionIdsValidationSucceeded(_logger);

            UsersLogMessages.RoleIdsValidating(_logger, updateModel.AssignedRoleIds);
            var assignedRoleIdsValidationResult = await _rolesService.ValidateIdsAsync(updateModel.AssignedRoleIds, cancellationToken);

            if (assignedRoleIdsValidationResult.IsFailure)
            {
                UsersLogMessages.RoleIdsValidationFailed(_logger, assignedRoleIdsValidationResult);
                return(assignedRoleIdsValidationResult);
            }
            UsersLogMessages.RoleIdsValidationSucceeded(_logger);

            var now = _systemClock.UtcNow;

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)UserManagementAdministrationActionType.UserModified,
                now,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var anyChanges = false;

            UsersLogMessages.UserPermissionMappingIdentitiesFetching(_logger, userId);
            var permissionMappings = await _usersRepository.AsyncEnumeratePermissionMappingIdentities(
                userId : userId,
                isDeleted : false)
                                     .ToArrayAsync(cancellationToken);

            UsersLogMessages.UserPermissionMappingIdentitiesFetched(_logger, userId);

            anyChanges |= await HandleRemovedPermissionMappings(
                userId,
                permissionMappings,
                updateModel.GrantedPermissionIds,
                updateModel.DeniedPermissionIds,
                actionId,
                cancellationToken);

            anyChanges |= await HandleAddedGrantedPermissions(
                permissionMappings,
                updateModel.GrantedPermissionIds,
                userId,
                actionId,
                cancellationToken);

            anyChanges |= await HandleAddedDeniedPermissions(
                permissionMappings,
                updateModel.DeniedPermissionIds,
                userId,
                actionId,
                cancellationToken);

            UsersLogMessages.UserRoleMappingIdentitiesFetching(_logger, userId);
            var roleMappings = await _usersRepository.AsyncEnumerateRoleMappingIdentities(
                userId : userId,
                isDeleted : false)
                               .ToArrayAsync(cancellationToken);

            UsersLogMessages.UserRoleMappingIdentitiesFetched(_logger, userId);

            anyChanges |= await HandleRemovedRoleMappings(
                userId,
                roleMappings,
                updateModel.AssignedRoleIds,
                actionId,
                cancellationToken);

            anyChanges |= await HandleAddedRoles(
                roleMappings,
                updateModel.AssignedRoleIds,
                userId,
                actionId,
                cancellationToken);

            if (!anyChanges)
            {
                UsersLogMessages.UserUpdateNoChangesGiven(_logger, userId);
                return(new NoChangesGivenError($"User ID {userId}"));
            }

            UsersLogMessages.UserUpdatingNotificationPublishing(_logger, userId);
            await _messenger.PublishNotificationAsync(
                new UserUpdatingNotification(
                    userId,
                    actionId),
                cancellationToken);

            UsersLogMessages.UserUpdatingNotificationPublished(_logger, userId);

            TransactionsLogMessages.TransactionScopeCommitting(_logger);
            transactionScope.Complete();

            UsersLogMessages.UserUpdated(_logger, userId);
            return(OperationResult.Success);
        }
        public async Task TrackUserAsync(
            ulong userId,
            string username,
            string discriminator,
            string avatarHash,
            CancellationToken cancellationToken)
        {
            UsersLogMessages.UserTracking(_logger, userId, username, discriminator, avatarHash);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var now = _systemClock.UtcNow;

            var mergeResult = await _usersRepository.MergeAsync(
                userId,
                username,
                discriminator,
                avatarHash,
                firstSeen : now,
                lastSeen : now,
                cancellationToken);

            if (mergeResult.RowsInserted > 0)
            {
                UsersLogMessages.UserCreated(_logger, userId);

                var actionId = await _auditableActionsRepository.CreateAsync(
                    (int)UserManagementAdministrationActionType.UserCreated,
                    now,
                    userId,
                    cancellationToken);

                AuditingLogMessages.AuditingActionCreated(_logger, actionId);

                UsersLogMessages.DefaultPermissionIdsFetching(_logger);
                var defaultPermissionIds = await _usersRepository
                                           .AsyncEnumerateDefaultPermissionIds()
                                           .ToArrayAsync(cancellationToken);

                UsersLogMessages.DefaultPermissionIdsFetched(_logger, defaultPermissionIds);

                if (defaultPermissionIds.Any())
                {
                    UsersLogMessages.UserPermissionMappingsCreating(_logger, userId, defaultPermissionIds, PermissionMappingType.Granted);
                    var mappingIds = await _usersRepository.CreatePermissionMappingsAsync(
                        userId,
                        defaultPermissionIds,
                        PermissionMappingType.Granted,
                        actionId,
                        cancellationToken);

                    UsersLogMessages.UserPermissionMappingsCreated(_logger, userId, mappingIds);
                }

                UsersLogMessages.DefaultRoleIdsFetching(_logger);
                var defaultRoleIds = await _usersRepository
                                     .AsyncEnumerateDefaultRoleIds()
                                     .ToArrayAsync(cancellationToken);

                UsersLogMessages.DefaultRoleIdsFetched(_logger, defaultRoleIds);

                if (defaultRoleIds.Any())
                {
                    UsersLogMessages.UserRoleMappingsCreating(_logger, userId, defaultRoleIds);
                    var mappingIds = await _usersRepository.CreateRoleMappingsAsync(
                        userId,
                        defaultRoleIds,
                        actionId,
                        cancellationToken);

                    UsersLogMessages.UserRoleMappingsCreated(_logger, userId, mappingIds);
                }

                UsersLogMessages.UserInitializingNotificationPublishing(_logger, userId);
                await _messenger.PublishNotificationAsync(
                    new UserInitializingNotification(
                        userId,
                        actionId),
                    cancellationToken);

                UsersLogMessages.UserInitializingNotificationPublished(_logger, userId);
            }
            else
            {
                UsersLogMessages.UserUpdated(_logger, userId);
            }

            TransactionsLogMessages.TransactionScopeCommitting(_logger);
            transactionScope.Complete();

            UsersLogMessages.UserTracked(_logger, userId);
        }
Exemple #15
0
        public async Task <OperationResult> UpdateAsync(
            long roleId,
            RoleUpdateModel updateModel,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            RolesLogMessages.RoleUpdating(_logger, roleId, updateModel, performedById);

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var nameValidationResult = await ValidateNameAsync(updateModel.Name, roleId, cancellationToken);

            if (nameValidationResult.IsFailure)
            {
                RolesLogMessages.RoleNameValidationFailed(_logger, updateModel.Name, nameValidationResult);
                return(nameValidationResult);
            }
            RolesLogMessages.RoleNameValidationSucceeded(_logger, updateModel.Name);

            var grantedPermissionIdsValidationResult = await _permissionsService.ValidateIdsAsync(updateModel.GrantedPermissionIds, cancellationToken);

            if (grantedPermissionIdsValidationResult.IsFailure)
            {
                RolesLogMessages.PermissionIdsValidationFailed(_logger, updateModel.GrantedPermissionIds, grantedPermissionIdsValidationResult);
                return(grantedPermissionIdsValidationResult);
            }
            RolesLogMessages.PermissionIdsValidationSucceeded(_logger, updateModel.GrantedPermissionIds);

            var now = _systemClock.UtcNow;

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)RoleManagementAdministrationActionType.RoleModified,
                now,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var updateResult = await _rolesRepository.UpdateAsync(
                roleId : roleId,
                actionId : actionId,
                name : updateModel.Name,
                cancellationToken : cancellationToken);

            if (updateResult.IsFailure && !(updateResult.Error is NoChangesGivenError))
            {
                RolesLogMessages.RoleUpdateFailed(_logger, roleId, updateResult);
                return(updateResult);
            }
            RolesLogMessages.RoleUpdated(_logger, roleId, updateResult);

            var anyChanges = updateResult.IsSuccess;

            RolesLogMessages.RolePermissionMappingIdentitiesFetching(_logger, roleId);
            var permissionMappings = await _rolesRepository.AsyncEnumeratePermissionMappingIdentities(
                roleId : roleId,
                isDeleted : false)
                                     .ToArrayAsync(cancellationToken);

            RolesLogMessages.RolePermissionMappingIdentitiesFetched(_logger, roleId);

            anyChanges |= await HandleRemovedPermissionMappings(
                roleId,
                permissionMappings,
                updateModel.GrantedPermissionIds,
                actionId,
                cancellationToken);

            anyChanges |= await HandleAddedPermissions(
                permissionMappings,
                updateModel.GrantedPermissionIds,
                roleId,
                actionId,
                cancellationToken);

            if (!anyChanges)
            {
                RolesLogMessages.RoleUpdateNoChangesGiven(_logger, roleId);
                return(new NoChangesGivenError($"Role ID {roleId}"));
            }

            RolesLogMessages.RoleUpdatingNotificationPublishing(_logger, roleId);
            await _messenger.PublishNotificationAsync(
                new RoleUpdatingNotification(
                    roleId,
                    actionId),
                cancellationToken);

            RolesLogMessages.RoleUpdatingNotificationPublished(_logger, roleId);

            transactionScope.Complete();
            TransactionsLogMessages.TransactionScopeCommitted(_logger);

            _memoryCache.Remove(_getCurrentIdentitiesCacheKey);
            RolesLogMessages.RoleIdentitiesCacheCleared(_logger);

            return(OperationResult.Success);
        }
        public async Task <OperationResult> UpdateExperienceDiffsAsync(
            IReadOnlyList <int> experienceDiffs,
            ulong performedById,
            CancellationToken cancellationToken)
        {
            CharactersLogMessages.CharacterLevelDefinitionsUpdating(_logger);

            var totalExperience     = 0;
            var proposedDefinitions = experienceDiffs
                                      .Select((experienceDiff, index) => (
                                                  level: index + 2,
                                                  previousExperienceThreshold: totalExperience,
                                                  experienceThreshold: totalExperience += experienceDiff))
                                      .Prepend((
                                                   level: 0,
                                                   previousExperienceThreshold: -1,
                                                   experienceThreshold: 0))
                                      .ToArray();

            foreach (var(level, previousExperienceThreshold, experienceThreshold) in proposedDefinitions)
            {
                CharactersLogMessages.CharacterLevelDefinitionProposed(_logger, level, experienceThreshold, previousExperienceThreshold);
                if (experienceThreshold <= previousExperienceThreshold)
                {
                    CharactersLogMessages.CharacterLevelDefinitionValidationFailed(_logger, level, experienceThreshold, previousExperienceThreshold);
                    return(new InvalidLevelDefinitionError(
                               level,
                               experienceThreshold,
                               previousExperienceThreshold));
                }
            }

            using var transactionScope = _transactionScopeFactory.CreateScope();
            TransactionsLogMessages.TransactionScopeCreated(_logger);

            var currentDefinitions = await GetCurrentDefinitionsAsync(cancellationToken);

            CharactersLogMessages.CharacterLevelDefinitionsFetchedCurrent(_logger);

            var sequenceLength   = Math.Max(experienceDiffs.Count + 1, currentDefinitions.Count);
            var pairwiseSequence = Enumerable.Zip(
                currentDefinitions
                .PadEnd <CharacterLevelDefinitionViewModel?>(sequenceLength, null),
                proposedDefinitions
                .Select(x => x.ToNullable())
                .PadEnd(sequenceLength, null),
                (current, proposed) => (current, proposed));

            var actionId = await _auditableActionsRepository.CreateAsync(
                (int)CharacterManagementAdministrationActionType.LevelDefinitionsUpdated,
                _systemClock.UtcNow,
                performedById,
                cancellationToken);

            AuditingLogMessages.AuditingActionCreated(_logger, actionId);

            var anyChangesMade = false;

            foreach (var(current, proposed) in pairwiseSequence)
            {
                if (proposed is null)
                {
                    CharactersLogMessages.CharacterLevelDefinitionDeleting(_logger, current !.Level);
                    await _characterLevelsRepository.MergeDefinitionAsync(
                        current !.Level,
                        current !.ExperienceThreshold,
                        true,
                        actionId,
                        cancellationToken);

                    CharactersLogMessages.CharacterLevelDefinitionDeleted(_logger, current !.Level);

                    anyChangesMade = true;
                }
                else if ((current is null) || (current.ExperienceThreshold != proposed.Value.experienceThreshold))
                {
                    CharactersLogMessages.CharacterLevelDefinitionUpdating(_logger, proposed !.Value.level, proposed !.Value.experienceThreshold);
                    await _characterLevelsRepository.MergeDefinitionAsync(
                        proposed !.Value.level,
                        proposed !.Value.experienceThreshold,
                        false,
                        actionId,
                        cancellationToken);

                    CharactersLogMessages.CharacterLevelDefinitionUpdated(_logger, proposed !.Value.level, proposed !.Value.experienceThreshold);

                    anyChangesMade = true;
                }
            }

            if (!anyChangesMade)
            {
                CharactersLogMessages.CharacterLevelDefinitionsNoChangesGiven(_logger);
                return(new NoChangesGivenError("Character Level Definitions"));
            }

            transactionScope.Complete();
            TransactionsLogMessages.TransactionScopeCommitted(_logger);

            _memoryCache.Remove(_getCurrentDefinitionsCacheKey);
            CharactersLogMessages.CharacterLevelDefinitionsCacheCleared(_logger);

            return(OperationResult.Success);
        }