예제 #1
0
        public void Execute(string[] _)
        {
            using var tx = _WorkflowDb.BeginTransaction();

            ForceWorkflowAuth();
            _WorkflowDb.SaveAndCommit();
        }
예제 #2
0
        public async Task <EnrollmentResponse> Execute()
        {
            var validDate = _DateTimeProvider.Now().AddDays(1); //TODO smells like a setting

            var entity = new KeyReleaseWorkflowState
            {
                LabConfirmationId = _NumberGenerator.GenerateToken(),
                Created           = _DateTimeProvider.Now(),
                BucketId          = Convert.ToBase64String(_NumberGenerator.GenerateKey()),
                ConfirmationKey   = Convert.ToBase64String(_NumberGenerator.GenerateKey()),
                ValidUntil        = new DateTime(validDate.Year, validDate.Month, validDate.Day, 4, 0, 0, DateTimeKind.Local) //TODO smells like a setting
            };

            _Logger.LogDebug("Writing.");
            await _DbContextProvider.KeyReleaseWorkflowStates.AddAsync(entity);

            _Logger.LogDebug("Committing.");
            _DbContextProvider.SaveAndCommit();
            _Logger.LogDebug("Committed.");

            return(new EnrollmentResponse
            {
                ConfirmationKey = entity.ConfirmationKey,
                BucketId = entity.BucketId,
                LabConfirmationId = $"{entity.LabConfirmationId.Substring(0, 3)}-{entity.LabConfirmationId.Substring(3, 3)}", //TODO UI concern in DB!
                Validity = (long)(entity.ValidUntil - _DateTimeProvider.Now()).TotalSeconds
            });
        }
        private bool WriteAttempt(TekReleaseWorkflowStateEntity wf)
        {
            if (++_AttemptCount > AttemptCountMax)
            {
                throw new InvalidOperationException("Maximum attempts reached.");
            }

            if (_AttemptCount > 1)
            {
                _Logger.LogWarning("Duplicate PollToken found - attempt:{AttemptCount}", _AttemptCount);
            }

            wf.PollToken = _PollTokenService.Next();

            try
            {
                _WorkflowDb.SaveAndCommit();
                _Logger.LogDebug("Committed.");
                return(true);
            }
            catch (DbUpdateException ex)
            {
                if (CanRetry(ex))
                {
                    return(false);
                }

                throw;
            }
        }
        private bool WriteAttempt(TekReleaseWorkflowStateEntity item)
        {
            if (++_AttemptCount > AttemptCountMax)
            {
                _Logger.WriteMaximumCreateAttemptsReached();
                throw new InvalidOperationException("Maximum create attempts reached.");
            }

            if (_AttemptCount > 1)
            {
                _Logger.WriteDuplicatesFound(_AttemptCount);
            }


            item.LabConfirmationId = _LabConfirmationIdService.Next();
            item.BucketId          = _NumberGenerator.NextByteArray(_WorkflowConfig.BucketIdLength);
            item.ConfirmationKey   = _NumberGenerator.NextByteArray(_WorkflowConfig.ConfirmationKeyLength);

            try
            {
                _WorkflowDbContext.SaveAndCommit();
                _Logger.WriteCommitted();
                return(true);
            }
            catch (DbUpdateException ex)
            {
                _WorkflowDbContext.Remove(item);
                if (CanRetry(ex))
                {
                    return(false);
                }

                throw;
            }
        }
        private bool WriteAttempt(TekReleaseWorkflowStateEntity wf)
        {
            if (++_attemptCount > AttemptCountMax)
            {
                throw new InvalidOperationException("Maximum attempts reached.");
            }

            if (_attemptCount > 1)
            {
                _logger.WriteDuplicatePollTokenFound(_attemptCount);
            }

            wf.PollToken = _pollTokenService.Next();

            try
            {
                _workflowDb.SaveAndCommit();
                _logger.WritePollTokenCommit();
                return(true);
            }
            catch (DbUpdateException ex)
            {
                if (CanRetry(ex))
                {
                    return(false);
                }

                throw;
            }
        }
예제 #6
0
        public async Task <LabVerifyAuthorisationResponse> Execute(LabVerifyArgs args)
        {
            if (!_PollTokens.Validate(args.PollToken))
            {
                throw new ArgumentException("Not valid.", nameof(args));
            }

            var wf = await _DbContextProvider.KeyReleaseWorkflowStates
                     .Include(x => x.Keys)
                     .FirstOrDefaultAsync(state =>
                                          state.PollToken == args.PollToken);

            if (wf == null)
            {
                var message = $"KeyReleaseWorkflowState not found - PollToken:{args.PollToken}.";
                _Logger.LogError(message);
                return(new LabVerifyAuthorisationResponse {
                    Error = "Workflow not found.", Valid = false
                });
            }

            var refreshedToken = _PollTokens.GenerateToken();

            wf.PollToken = refreshedToken;
            _Logger.LogDebug($"Committing.");
            _DbContextProvider.SaveAndCommit();

            _Logger.LogInformation($"Committed - new PollToken:{wf.PollToken}.");
            return(new LabVerifyAuthorisationResponse
            {
                PollToken = refreshedToken, Valid = wf.Keys?.Any() ?? false
            });
        }
        private async Task InnerExecute(byte[] signature, HttpRequest request)
        {
            try
            {
                if (signature == null)
                {
                    _Logger.LogError("Signature error: null.");
                    return;
                }

                if (signature.Length == 0)
                {
                    _Logger.LogError("Signature error: Zero length.");
                    return;
                }

                using var reader = new StreamReader(request.Body);
                var payload = await reader.ReadToEndAsync();

                var args = _JsonSerializer.Deserialize <ReleaseTeksArgs>(payload);

                _Logger.LogDebug($"Body: {args}.");

                var workflow = _DbContextProvider
                               .KeyReleaseWorkflowStates
                               .FirstOrDefault(x => x.BucketId == args.BucketId);

                if (workflow == null)
                {
                    _Logger.LogError("Matching workflow not found.");
                    return;
                }

                _Logger.LogDebug("Matching workflow found.");

                if (!_KeyValidator.Validate(args, workflow))
                {
                    _Logger.LogError("Keys not valid.");
                    return;
                }

                if (!_SignatureValidator.Valid(signature, workflow, Encoding.UTF8.GetBytes(payload)))
                {
                    _Logger.LogError($"Signature not valid: {Convert.ToBase64String(signature)}.");
                    return;
                }

                _Logger.LogDebug("Writing.");
                await _Writer.Execute(args);

                _Logger.LogDebug("Committing.");
                _DbContextProvider.SaveAndCommit();
                _Logger.LogDebug("Committed.");
            }
            catch (Exception e)
            {
                _Logger.LogError(e.ToString());
            }
        }
        public async Task <IActionResult> Execute(AuthorisationArgs args)
        {
            await _AuthorisationWriter.Execute(args);

            _DbContextProvider.SaveAndCommit();

            return(new OkObjectResult(new AuthorisationResponse {
                Valid = true
            }));
        }
예제 #9
0
        private async Task GenWorkflows()
        {
            for (var i = 0; i < _Args.WorkflowCount; i++)
            {
                _WorkflowDb.BeginTransaction();
                var workflow = await _RegisterWriter().Execute(); //Save/Commit

                _WorkflowDb.BeginTransaction();
                GenTeks(workflow);
                _WorkflowDb.SaveAndCommit();
            }
        }
        public async Task <IActionResult> Execute(KeysLastAuthorisationArgs args)
        {
            if (!_Validator.Valid(args.UploadAuthorisationToken)) //TODO check validation
            {
                return(new OkResult());
            }

            await _AuthorisationWriter.Execute(args);

            _DbContextProvider.SaveAndCommit();
            return(new OkResult());
        }
        public async Task <IActionResult> Execute()
        {
            var entityType = _DbContextProvider.Model.FindEntityType(typeof(KeyReleaseWorkflowState));
            var schema     = entityType.GetSchema();
            var tableName  = entityType.GetTableName();
            var query      = $"DELETE FROM [{schema}].[{tableName}] WHERE {nameof(KeyReleaseWorkflowState.ValidUntil)} <= GETDATE()";
            await _DbContextProvider.Database.ExecuteSqlRawAsync(query);

            _DbContextProvider.SaveAndCommit();

            return(new OkObjectResult(true));
        }
        public async Task <IActionResult> Execute(byte[] signature, string payload)
        {
            var args = JsonConvert.DeserializeObject <KeysLastReleaseTeksArgs>(payload);

            if (!_KeyValidator.Validate(args) || !_SignatureValidator.Valid(signature, args.BucketId, Encoding.UTF8.GetBytes(payload)))
            {
                return(new OkResult());
            }

            await _Writer.Execute(args);

            _DbContextProvider.SaveAndCommit();

            return(new OkResult());
        }
예제 #13
0
        public void Execute()
        {
            var expired = _DateTimeProvider.Now() - TimeSpan.FromMinutes(_KeysLastWorkflowConfig.AuthorisationWindowDurationMinutes);

            _DbContextProvider.BeginTransaction();

            throw new NotImplementedException();

            //var q = _DbContextProvider.KeysLastWorkflows
            //    .Where(x => x.State == KeysLastWorkflowState.Receiving && x.AuthorisationWindowStart < expired);

            //_DbContextProvider.KeysLastWorkflows.RemoveRange(q);
            _DbContextProvider.SaveChanges();
            _DbContextProvider.SaveAndCommit();
        }
예제 #14
0
        public void Execute()
        {
            var expired = _DateTimeProvider.Now() - TimeSpan.FromDays(_KeysLastWorkflowConfig.SecretLifetimeDays);

            _DbContextProvider.BeginTransaction();

            throw new NotImplementedException();

            //var q = _DbContextProvider.KeysLastWorkflows
            //    .Where(x => x.State == KeysLastWorkflowState.Unauthorised && x.Created < expired);

            //_DbContextProvider.KeysLastWorkflows.RemoveRange(q);

            _DbContextProvider.SaveChanges();
            _DbContextProvider.SaveAndCommit();
        }
예제 #15
0
        public async Task <IActionResult> Execute(KeysFirstEscrowArgs args)
        {
            if (!_KeysFirstEscrowValidator.Validate(args))
            {
                return(new BadRequestResult());
            }

            await using (var tx = _DbContextProvider.BeginTransaction())
            {
                await _KeysFirstEscrowDbInsertCommand.Execute(args);

                _DbContextProvider.SaveAndCommit();
            }

            return(new OkResult());
        }
예제 #16
0
        public async Task AddExampleContent()
        {
            var r = new Random();

            await using var tx = await _Provider.Database.BeginTransactionAsync();

            var wfs1 = new KeyReleaseWorkflowState
            {
                LabConfirmationId = "2L2587",
                BucketId          = "2",
                ConfirmationKey   = "3",
                Created           = new DateTime(2020, 5, 1),
            };

            var key1 = new TemporaryExposureKeyEntity
            {
                Owner                 = wfs1,
                PublishingState       = PublishingState.Unpublished,
                RollingPeriod         = 1,
                RollingStartNumber    = 1,
                TransmissionRiskLevel = 0,
                KeyData               = new byte[16],
                Region                = "NL"
            };

            r.NextBytes(key1.KeyData);

            var key2 = new TemporaryExposureKeyEntity
            {
                Owner                 = wfs1,
                PublishingState       = PublishingState.Unpublished,
                RollingPeriod         = 1,
                RollingStartNumber    = 1,
                TransmissionRiskLevel = 0,
                KeyData               = new byte[16],
                Region                = "NL"
            };

            r.NextBytes(key1.KeyData);

            await _Provider.KeyReleaseWorkflowStates.AddAsync(wfs1);

            await _Provider.TemporaryExposureKeys.AddRangeAsync(key1, key2);

            _Provider.SaveAndCommit();
        }
        public async Task Execute(int pAuthorize, Random r)
        {
            _DbContextProvider.BeginTransaction();
            var unauthorized = _DbContextProvider.Set <KeyReleaseWorkflowState>()
                               .Where(x => x.Authorised == false)
                               .Select(x => x.ConfirmationKey)
                               .ToArray();

            var authorized = unauthorized
                             .Where(x => r.Next(100) <= pAuthorize);

            foreach (var i in authorized)
            {
                await _Writer.Execute(new KeysFirstAuthorisationArgs { Token = i });
            }

            _DbContextProvider.SaveAndCommit();
        }
예제 #18
0
        public async Task AddExampleContent()
        {
            var r2 = new StandardRandomNumberGenerator();

            await using var tx = await _Provider.Database.BeginTransactionAsync();

            var wfs1 = new TekReleaseWorkflowStateEntity
            {
                LabConfirmationId = _LabConfirmationIdService.Next(),
                BucketId          = r2.NextByteArray(_WorkflowConfig.BucketIdLength),
                ConfirmationKey   = r2.NextByteArray(_WorkflowConfig.ConfirmationKeyLength),
                Created           = new DateTime(2020, 5, 1, 0, 0, 0, DateTimeKind.Utc),
            };

            var key1 = new TekEntity
            {
                Owner              = wfs1,
                PublishingState    = PublishingState.Unpublished,
                RollingPeriod      = 2,
                RollingStartNumber = DateTime.UtcNow.Date.ToRollingStartNumber(),
                KeyData            = r2.NextByteArray(_TekValidatorConfig.KeyDataLength),
                Region             = "NL"
            };


            var key2 = new TekEntity
            {
                Owner              = wfs1,
                PublishingState    = PublishingState.Unpublished,
                RollingPeriod      = 144,
                RollingStartNumber = DateTime.UtcNow.Date.ToRollingStartNumber(),
                KeyData            = r2.NextByteArray(_TekValidatorConfig.KeyDataLength),
                Region             = "NL"
            };

            await _Provider.KeyReleaseWorkflowStates.AddAsync(wfs1);

            await _Provider.TemporaryExposureKeys.AddRangeAsync(key1, key2);

            _Provider.SaveAndCommit();
        }
예제 #19
0
        public async Task Execute(byte[] signature, HttpRequest request)
        {
            using var reader = new StreamReader(request.Body);
            var payload = await reader.ReadToEndAsync();

            var args = JsonConvert.DeserializeObject <ReleaseTeksArgs>(payload);

            var workflow = _DbContextProvider
                           .KeyReleaseWorkflowStates
                           .FirstOrDefault(x => x.BucketId == args.BucketId);

            if (workflow == null || !_KeyValidator.Validate(args, workflow) ||
                !_SignatureValidator.Valid(signature, workflow, Encoding.UTF8.GetBytes(payload)))
            {
                return;
            }

            await _Writer.Execute(args);

            _DbContextProvider.SaveAndCommit();
        }
예제 #20
0
        private bool TryGenerateRemainingFieldsAndWriteToDb(TekReleaseWorkflowStateEntity item)
        {
            if (++_AttemptCount > AttemptCountMax)
            {
                _logger.WriteMaximumCreateAttemptsReached();
                throw new InvalidOperationException("Maximum create attempts reached.");
            }

            if (_AttemptCount > 1)
            {
                _logger.WriteDuplicatesFound(_AttemptCount);
            }

            item.GGDKey          = _luhnModNGenerator.Next(_luhnModNConfig.ValueLength);
            item.BucketId        = _numberGenerator.NextByteArray(UniversalConstants.BucketIdByteCount);
            item.ConfirmationKey = _numberGenerator.NextByteArray(UniversalConstants.ConfirmationKeyByteCount);

            try
            {
                _workflowDbContext.SaveAndCommit();
                _logger.WriteCommitted();
                return(true);
            }
            catch (DbUpdateException ex)
            {
                _workflowDbContext.Database.CurrentTransaction.RollbackAsync();
                _workflowDbContext.Remove(item);

                if (CanRetry(ex))
                {
                    return(false);
                }

                throw;
            }
        }
예제 #21
0
        public async Task <EnrollmentResponse> Execute()
        {
            var entity = new KeyReleaseWorkflowState
            {
                LabConfirmationId = _NumberGenerator.GenerateToken(),
                Created           = _DateTimeProvider.Now(),
                BucketId          = Convert.ToBase64String(_NumberGenerator.GenerateKey()),
                ConfirmationKey   = Convert.ToBase64String(_NumberGenerator.GenerateKey()),
            };

            await _DbContextProvider.KeyReleaseWorkflowStates.AddAsync(entity);

            _DbContextProvider.SaveAndCommit();

            var validDate = DateTime.Now.AddDays(1);

            return(new EnrollmentResponse
            {
                ConfirmationKey = entity.ConfirmationKey,
                BucketId = entity.BucketId,
                LabConfirmationId = entity.LabConfirmationId,
                ValidUntil = new DateTime(validDate.Year, validDate.Month, validDate.Day, 4, 0, 0, DateTimeKind.Local)
            });
        }
예제 #22
0
        private async Task InnerExecute(byte[] signature, byte[] body)
        {
            _BodyBytes = body;

            if ((signature?.Length ?? 0) != _WorkflowConfig.PostKeysSignatureLength)
            {
                _Logger.WriteSignatureValidationFailed();
                return;
            }

            try
            {
                var argsJson = Encoding.UTF8.GetString(_BodyBytes);
                _ArgsObject = _JsonSerializer.Deserialize <PostTeksArgs>(argsJson);
            }
            catch (Exception e)
            {
                _Logger.WritePostBodyParsingFailed(e);
                return;
            }

            var base64Parser       = new Base64();
            var base64ParserResult = base64Parser.TryParseAndValidate(_ArgsObject.BucketId, _WorkflowConfig.BucketIdLength);

            if (!base64ParserResult.Valid)
            {
                _Logger.WriteBucketIdParsingFailed(_ArgsObject.BucketId, base64ParserResult.Messages);
                return;
            }

            _BucketIdBytes = base64ParserResult.Item;

            var messages = _KeyValidator.Validate(_ArgsObject);

            if (messages.Length > 0)
            {
                _Logger.WriteTekValidationFailed(messages);
                return;
            }

            var teks = _ArgsObject.Keys.Select(Mapper.MapToTek).ToArray();

            foreach (var i in teks)
            {
                i.PublishAfter = _DateTimeProvider.Snapshot;
            }

            messages = new TekListDuplicateValidator().Validate(teks);
            if (messages.Length > 0)
            {
                _Logger.WriteTekDuplicatesFound(messages);
                return;
            }

            //Validation ends, filtering starts

            var filterResult = _TekApplicableWindowFilter.Execute(teks);

            _Logger.WriteApplicableWindowFilterResult(filterResult.Messages);
            teks = filterResult.Items;
            _Logger.WriteValidTekCount(teks.Length);

            var workflow = _DbContext
                           .KeyReleaseWorkflowStates
                           .Include(x => x.Teks)
                           .SingleOrDefault(x => x.BucketId == _BucketIdBytes);

            if (workflow == null)
            {
                _Logger.WriteBucketDoesNotExist(_ArgsObject.BucketId);
                return;
            }

            if (!_SignatureValidator.Valid(signature, workflow.ConfirmationKey, _BodyBytes))
            {
                _Logger.WriteSignatureInvalid(workflow.BucketId, signature);
                return;
            }

            var filterResults = _TekListWorkflowFilter.Filter(teks, workflow);

            _Logger.WriteWorkflowFilterResults(filterResults.Messages);
            _Logger.WriteValidTekCountSecondPass(teks.Length);

            //Run after the filter removes the existing TEKs from the args.
            var allTeks = workflow.Teks.Select(Mapper.MapToTek).Concat(filterResults.Items).ToArray();

            messages = new TekListDuplicateKeyDataValidator().Validate(allTeks);
            if (messages.Length > 0)
            {
                _Logger.WriteTekDuplicatesFoundWholeWorkflow(messages);
                return;
            }

            _Logger.WriteDbWriteStart();
            var writeArgs = new TekWriteArgs
            {
                WorkflowStateEntityEntity = workflow,
                NewItems = filterResults.Items
            };

            await _Writer.Execute(writeArgs);

            _DbContext.SaveAndCommit();
            _Logger.WriteDbWriteCommitted();

            if (filterResults.Items.Length != 0)
            {
                _Logger.WriteTekCountAdded(filterResults.Items.Length);
            }
        }
        private async Task CommitResults()
        {
            _Logger.LogInformation($"Commit results - publish EKSs.");

            await using (_PublishingDbContext.BeginTransaction())
            {
                var move = _PublishingDbContext.Set <EksCreateJobOutputEntity>().Select(
                    x => new ExposureKeySetContentEntity
                {
                    Created              = _Start,
                    Release              = x.Release,
                    ContentTypeName      = MediaTypeNames.Application.Zip,
                    Content              = x.Content,
                    CreatingJobName      = x.CreatingJobName,
                    CreatingJobQualifier = x.CreatingJobQualifier,
                    PublishingId         = _PublishingId.Create(x.Content)
                }).ToArray();

                await using (_ContentDbContext.BeginTransaction())
                {
                    _ContentDbContext.Set <ExposureKeySetContentEntity>().AddRange(move);
                    _ContentDbContext.SaveAndCommit();
                }
            }

            _Logger.LogInformation($"Commit results - Mark TEKs as Published.");

            await using (_PublishingDbContext.BeginTransaction()) //Read-only
            {
                await using (_WorkflowDbContext.BeginTransaction())
                {
                    var count = 0;
                    var used  = _PublishingDbContext.Set <EksCreateJobInputEntity>()
                                .Where(x => x.Used)
                                .Skip(count)
                                .Select(x => x.Id)
                                .Take(100)
                                .ToArray();

                    while (used.Length > 0)
                    {
                        var zap = _WorkflowDbContext.TemporaryExposureKeys
                                  .Where(x => used.Contains(x.Id))
                                  .ToList();

                        foreach (var i in zap)
                        {
                            i.PublishingState = PublishingState.Published;
                        }

                        await _WorkflowDbContext.BulkUpdateAsync(zap, x => x.PropertiesToInclude = new List <string> {
                            nameof(TemporaryExposureKeyEntity.PublishingState)
                        });

                        count += used.Length;

                        used = _PublishingDbContext.Set <EksCreateJobInputEntity>()
                               .Where(x => x.Used)
                               .Skip(count)
                               .Select(x => x.Id)
                               .Take(100)
                               .ToArray();
                    }

                    _WorkflowDbContext.SaveAndCommit();
                }

                _Logger.LogInformation($"Cleanup job tables.");
                await _PublishingDbContext.Set <EksCreateJobInputEntity>().BatchDeleteAsync();

                await _PublishingDbContext.Set <EksCreateJobOutputEntity>().BatchDeleteAsync();

                _PublishingDbContext.SaveAndCommit();
            }
        }
        private async Task InnerExecute(byte[] signature, byte[] body)
        {
            _Logger.LogDebug("Signature received: {Signature}", signature);

            _BodyBytes = body;

            if ((signature?.Length ?? 0) != _WorkflowConfig.PostKeysSignatureLength)
            {
                _Logger.LogError("Signature is null or incorrect length.");
                return;
            }

            try
            {
                var argsJson = Encoding.UTF8.GetString(_BodyBytes);
                _Logger.LogDebug("Body -\n{ArgsJson}.", argsJson);
                _ArgsObject = _JsonSerializer.Deserialize <PostTeksArgs>(argsJson);
            }
            catch (Exception e)
            {
                _Logger.LogError("Error reading body -\n{E}", e.ToString());
                return;
            }

            var base64Parser       = new Base64();
            var base64ParserResult = base64Parser.TryParseAndValidate(_ArgsObject.BucketId, _WorkflowConfig.BucketIdLength);

            if (!base64ParserResult.Valid)
            {
                _Logger.LogValidationMessages(base64ParserResult.Messages.Select(x => $"BuckedId - {x}").ToArray());
                return;
            }

            _BucketIdBytes = base64ParserResult.Item;

            if (_Logger.LogValidationMessages(_KeyValidator.Validate(_ArgsObject)))
            {
                return;
            }

            var teks = _ArgsObject.Keys.Select(Mapper.MapToTek).ToArray();

            foreach (var i in teks)
            {
                i.PublishAfter = _DateTimeProvider.Snapshot;
            }

            if (_Logger.LogValidationMessages(new TekListDuplicateValidator().Validate(teks)))
            {
                return;
            }

            //Validation ends, filtering starts

            var filterResult = _TekApplicableWindowFilter.Execute(teks);

            teks = filterResult.Items;
            _Logger.LogFilterMessages(filterResult.Messages);
            _Logger.LogInformation("TEKs remaining - Count:{count}.", teks.Length);

            var workflow = _DbContext
                           .KeyReleaseWorkflowStates
                           .Include(x => x.Teks)
                           .SingleOrDefault(x => x.BucketId == _BucketIdBytes);

            if (workflow == null)
            {
                _Logger.LogError("Workflow does not exist - {BucketId}.", _ArgsObject.BucketId);
                return;
            }

            if (!_SignatureValidator.Valid(signature, workflow.ConfirmationKey, _BodyBytes))
            {
                _Logger.LogError("Signature not valid: {Signature}", signature);
                return;
            }

            var filterResults = _TekListWorkflowFilter.Filter(teks, workflow);

            _Logger.LogFilterMessages(filterResults.Messages);
            _Logger.LogInformation("TEKs remaining - Count:{count}.", teks.Length);

            //Run after the filter removes the existing TEKs from the args.
            var allTeks = workflow.Teks.Select(Mapper.MapToTek).Concat(filterResults.Items).ToArray();

            if (_Logger.LogValidationMessages(new TekListDuplicateKeyDataValidator().Validate(allTeks)))
            {
                return;
            }

            _Logger.LogDebug("Writing.");
            var writeArgs = new TekWriteArgs
            {
                WorkflowStateEntityEntity = workflow,
                NewItems = filterResults.Items
            };

            await _Writer.Execute(writeArgs);

            _DbContext.SaveAndCommit();
            _Logger.LogDebug("Committed.");

            if (filterResults.Items.Length != 0)
            {
                _Logger.LogInformation("Teks added - Count:{FilterResultsLength}.", filterResults.Items.Length);
            }
        }
        public async Task CommitResults()
        {
            await using (_ContentDbContext.BeginTransaction())
            {
                var move = _ContentDbContext.EksOutput.Select(
                    x => new ExposureKeySetContentEntity
                {
                    Release              = x.Release,
                    Content              = x.Content,
                    ContentTypeName      = MediaTypeNames.Application.Zip,
                    CreatingJobName      = x.CreatingJobName,
                    CreatingJobQualifier = x.CreatingJobQualifier,
                    PublishingId         = _PublishingId.Create(x.Content)
                }).ToArray();     //TODO copy? Cos it's the same DB cos 'policy'
                _ContentDbContext.ExposureKeySetContent.AddRange(move);
                _ContentDbContext.SaveAndCommit();
            }

            await using (_ContentDbContext.BeginTransaction()) //Read-only
                await using (_WorkflowDbContext.BeginTransaction())
                {
                    var count = 0;
                    var used  = _ContentDbContext.Set <EksCreateJobInputEntity>()
                                .Where(x => x.Used)
                                .Skip(count)
                                .Select(x => x.Id)
                                .Take(100)
                                .ToArray();

                    while (used.Length > 0)
                    {
                        var zap = _WorkflowDbContext.TemporaryExposureKeys
                                  .Where(x => used.Contains(x.Id))
                                  .ToList();

                        foreach (var i in zap)
                        {
                            i.PublishingState = PublishingState.Published;
                        }

                        await _WorkflowDbContext.BulkUpdateAsync(zap, x => x.PropertiesToInclude = new List <string>() { nameof(TemporaryExposureKeyEntity.PublishingState) });

                        count += used.Length;

                        used = _ContentDbContext.Set <EksCreateJobInputEntity>()
                               .Where(x => x.Used)
                               .Skip(count)
                               .Select(x => x.Id)
                               .Take(100)
                               .ToArray();
                    }

                    _WorkflowDbContext.SaveAndCommit();
                }

            await _ContentDbContext.EksInput.BatchDeleteAsync();

            await _ContentDbContext.EksOutput.BatchDeleteAsync();

            _ContentDbContext.SaveAndCommit();
        }