public async Task <QueryResult <string> > Handle(UnencryptStepFieldQuery request, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); if (request.Type != StepEncryptionTypes.Inputs && request.Type != StepEncryptionTypes.Outputs) { throw new InvalidUnencryptionRequestException("No such encryption type " + request.Type); } var step = await _entitiesRepository.GetFirstOrDefaultAsync <Step>(s => s.Id == request.StepId); if (step.CreatedBy != request.UserId) { throw new InvalidStepPermissionException("Only the creating user can decrypt the step secret."); } var stepTemplate = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == step.StepTemplateId); //Compare the to lower of inputs, TODO - this is inefficient if (!stepTemplate.InputDefinitions.ContainsKey(request.FieldName.ToLower())) { throw new InvalidUnencryptionRequestException("Field " + request.FieldName + " does not exist on step template " + stepTemplate.Id); } DynamicDataDescription kv = request.Type == StepEncryptionTypes.Inputs ? stepTemplate.InputDefinitions[request.FieldName.ToLower()] : stepTemplate.OutputDefinitions[request.FieldName.ToLower()]; if (kv.Type != InputDataTypes.Secret) { throw new InvalidUnencryptionRequestException("Field " + request.FieldName + " is not a secret type."); } var decryptedInput = DynamicDataUtility.DecryptDynamicData(stepTemplate.InputDefinitions, request.Type == StepEncryptionTypes.Inputs ? step.Inputs : step.Outputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey(), false); stopwatch.Stop(); return(new QueryResult <string>() { ElapsedMs = stopwatch.ElapsedMilliseconds, Count = 1, Result = (string)decryptedInput[request.FieldName.ToLower()] }); }
public async Task <CommandResult <Step> > Handle(AssignStepCommand request, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); List <Guid> ignoreUnassignedSteps = new List <Guid>(); if (_clusterStateService.GetSettings.AssignmentEnabled) { var assignedStepSuccessfully = false; Step unassignedStep = null; var dateChecked = DateTime.UtcNow; BotKey botkey; if (!_cache.TryGetValue(request.BotId, out botkey)) { // Set cache options. var cacheEntryOptions = new MemoryCacheEntryOptions() // Keep in cache for this time, reset time if accessed. .SetSlidingExpiration(TimeSpan.FromSeconds(10)); botkey = await _entitiesRepository.GetFirstOrDefaultAsync <BotKey>(bk => bk.Id == request.BotId); // Save data in cache. _cache.Set(request.BotId, botkey, cacheEntryOptions); } if (botkey.IsDisabled) { return(new CommandResult <Step>(new BotKeyAssignmentException("Bot " + botkey.Id + " is disabled.")) { Type = CommandResultTypes.Update, ElapsedMs = stopwatch.ElapsedMilliseconds }); } ignoreUnassignedSteps.AddRange(_clusterStateService.GetState().Locks.Where(l => l.Key.Contains("_object")).Select(ol => new Guid(ol.Key.Split(':').Last()))); do { unassignedStep = (await _entitiesRepository.GetAsync <Step>(s => s.Status == StepStatuses.Unassigned && request.StepTemplateIds.Contains(s.StepTemplateId) && !ignoreUnassignedSteps.Contains(s.Id), null, "CreatedOn:1", 1, 0)).FirstOrDefault(); if (unassignedStep != null) { var assigned = await _node.Handle(new RequestDataShard() { Type = unassignedStep.ShardType, ObjectId = unassignedStep.Id, CreateLock = true, LockTimeoutMs = 10000 }); //Apply a lock on the item if (assigned != null && assigned.IsSuccessful && assigned.AppliedLocked) { //Real values to pass to the Microservice Dictionary <string, object> realAssignedValues = new Dictionary <string, object>(); //Inputs that have been converted to reference expression Dictionary <string, object> convertedInputs = new Dictionary <string, object>(); var template = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == unassignedStep.StepTemplateId); try { //This should not throw a error externally, the server should loop to the next one and log a error if (unassignedStep.Status != StepStatuses.Unassigned) { throw new InvalidStepQueueException("You cannot assign step " + unassignedStep.Id + " as it is not unassigned."); } bool inputsUpdated = false; foreach (var input in unassignedStep.Inputs) { string convertedValue = ""; bool isReferenceByValue = false; var isReference = InputDataUtility.IsInputReference(input, out convertedValue, out isReferenceByValue); if (input.Value is string && ((string)input.Value).Length > 1) { if (isReference) { //Copy by reference if (isReferenceByValue) { var foundGlobalValue = await _entitiesRepository.GetFirstOrDefaultAsync <GlobalValue>(gv => gv.Name == convertedValue); if (foundGlobalValue == null) { Logger.LogWarning("No global value was found for value " + input.Value); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, input.Value + ":?"); } else if (foundGlobalValue.Type != template.InputDefinitions[input.Key].Type) { Logger.LogWarning("Global value was found for value " + input.Value + " however they are different types. " + template.InputDefinitions[input.Key].Type + " vs " + foundGlobalValue.Type); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, input.Value + ":?"); } else { realAssignedValues.Add(input.Key, foundGlobalValue.Value); convertedInputs.Add(input.Key, input.Value + ":" + foundGlobalValue.Journal.GetCurrentChainId()); } } //copy by value else { var foundGlobalValue = await _entitiesRepository.GetFirstOrDefaultAsync <GlobalValue>(gv => gv.Name == convertedValue); if (foundGlobalValue == null) { Logger.LogWarning("No global value was found for value " + input.Value); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, null); } else if (foundGlobalValue.Type != template.InputDefinitions[input.Key].Type) { Logger.LogWarning("Global value was found for value " + input.Value + " however they are different types. " + template.InputDefinitions[input.Key].Type + " vs " + foundGlobalValue.Type); realAssignedValues.Add(input.Key, null); convertedInputs.Add(input.Key, null); } else { realAssignedValues.Add(input.Key, foundGlobalValue.Value); convertedInputs.Add(input.Key, foundGlobalValue.Value); } } inputsUpdated = true; } else if (input.Value is string && ((string)input.Value).Length > 1 && ((string)input.Value).First() == '\\') { var escapedCommand = ((string)input.Value); //The $ is escaped realAssignedValues.Add(input.Key, ((string)input.Value).Substring(1, escapedCommand.Length - 1)); convertedInputs.Add(input.Key, input.Value); inputsUpdated = true; } else { realAssignedValues.Add(input.Key, input.Value); convertedInputs.Add(input.Key, input.Value); } } else { realAssignedValues.Add(input.Key, input.Value); convertedInputs.Add(input.Key, input.Value); } } //If a update was detected then add it to the journal updates if (inputsUpdated) { unassignedStep.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry() { CreatedBy = SystemUsers.QUEUE_MANAGER, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { Type = UpdateType.Override, FieldName = "status", Value = StepStatuses.Assigned }, new Update() { FieldName = "inputs", Type = UpdateType.Override, Value = convertedInputs }, new Update() { FieldName = "assignedto", Type = UpdateType.Override, Value = request.BotId } } }); } else { unassignedStep.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry() { CreatedBy = SystemUsers.QUEUE_MANAGER, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { Type = UpdateType.Override, FieldName = "status", Value = StepStatuses.Assigned } } }); } await _node.Handle(new AddShardWriteOperation() { Data = unassignedStep, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update, RemoveLock = true, LockId = assigned.LockId.Value }); //await _entitiesRepository.UpdateStep(unassignedStep); if (inputsUpdated) { //Update the record with real values, this is not commited to DB unassignedStep.UpdateJournal(new Domain.Entities.JournalEntries.JournalEntry() { CreatedBy = SystemUsers.QUEUE_MANAGER, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "inputs", Type = UpdateType.Override, Value = realAssignedValues } } }); } } catch (Exception e) { Console.WriteLine(e.Message); //throw e; } assignedStepSuccessfully = true; } else { ignoreUnassignedSteps.Add(unassignedStep.Id); assignedStepSuccessfully = false; } } //There were no unassigned steps to assign else { assignedStepSuccessfully = true; } }while (!assignedStepSuccessfully); if (unassignedStep != null) { var template = await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == unassignedStep.StepTemplateId); //Decrypt the step unassignedStep.Inputs = DynamicDataUtility.DecryptDynamicData(template.InputDefinitions, unassignedStep.Inputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey()); unassignedStep.RemoveDelimiters(); //Encrypt the step unassignedStep.Inputs = DynamicDataUtility.EncryptDynamicData(template.InputDefinitions, unassignedStep.Inputs, EncryptionProtocol.RSA, botkey.PublicEncryptionKey, true); } stopwatch.Stop(); return(new CommandResult <Step>() { ObjectRefId = unassignedStep != null?unassignedStep.Id.ToString() : "", ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.Update, Result = unassignedStep != null ? unassignedStep : null }); } else { return(new CommandResult <Step>() { ObjectRefId = "", ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.None }); } }
/// <summary> /// Used for running the main loop /// </summary> public async void BotLoop() { Logger.LogDebug("Starting loops."); var stopWatch = new Stopwatch(); while (started) { Logger.LogDebug("Starting new Thread"); Step nextStep = null; string encryptionKey = ""; try { var response = await GetNextStep(); nextStep = response.Step; encryptionKey = response.EncryptionKey; } catch (Exception e) { Logger.LogWarning("Error getting next step, will sleep and try again. " + e.Message); } stopWatch.Reset(); stopWatch.Start(); UpdateStepRequest stepResult = new UpdateStepRequest(); string newEncryptionKey = SecurityUtility.RandomString(32, false); if (nextStep != null) { Logger.LogInformation("Processing step " + nextStep.Id); stepResult.Id = nextStep.Id; string decryptedEncryptionKey = encryptionKey != null && encryptionKey != "" ? SecurityUtility.RsaDecryptWithPrivate(encryptionKey, _client.keyPair.PrivateKey): ""; nextStep.Inputs = DynamicDataUtility.DecryptDynamicData(RegisteredTemplates.Where(rt => rt.ReferenceId == nextStep.StepTemplateId).First().InputDefinitions, nextStep.Inputs, Domain.Enums.EncryptionProtocol .AES256, decryptedEncryptionKey, false); var template = RegisteredTemplates.Where(rt => rt.ReferenceId == nextStep.StepTemplateId).First(); try { stepResult = await ProcessStep(nextStep); stepResult.Outputs = DynamicDataUtility.EncryptDynamicData(template.OutputDefinitions, stepResult.Outputs, Domain.Enums.EncryptionProtocol.AES256, newEncryptionKey); stepResult.EncryptionKey = SecurityUtility.RsaEncryptWithPrivate(newEncryptionKey, _client.keyPair.PrivateKey); } catch (Exception e) { await SendStepLog(nextStep.Id, "Encountered unexcepted error " + e.Message + Environment.NewLine + e.StackTrace); stepResult.Status = StepStatuses.Error; stepResult.Log = "Encountered uncaught error at " + e.Message + "."; } int count = 0; string success = ""; try { success = await _client.CompleteStep(stepResult, idToken); Logger.LogInformation("Completed step " + stepResult.Id + " with status " + stepResult.Status); } catch (Exception e) { Logger.LogError("Failed to save step " + stepResult.Id + " with status " + stepResult.Status + " in Cindi with exception " + e.Message + "."); Thread.Sleep(1000); } } else { Logger.LogDebug("No step found"); } loopNumber++; stopWatch.Stop(); Logger.LogDebug("Completed Service Loop " + loopNumber + " took approximately " + stopWatch.ElapsedMilliseconds + "ms"); //Only sleep if no step was picked up if (nextStep == null) { lock (waitTimeLocker) { Logger.LogDebug("Sleeping for " + waitTime + "ms"); Thread.Sleep(waitTime); } } } }
public async Task <CommandResult> Handle(ScanWorkflowCommand request, CancellationToken cancellationToken) { var stopwatch = new Stopwatch(); stopwatch.Start(); List <string> messages = new List <string>(); bool workflowStillRunning = false; var workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == request.WorkflowId); if (workflow == null) { throw new MissingWorkflowException("Failed to find workflow " + request.WorkflowId + " as workflow does not exist."); } //Get the workflow template var workflowTemplate = await _entitiesRepository.GetFirstOrDefaultAsync <WorkflowTemplate>(wt => wt.ReferenceId == workflow.WorkflowTemplateId); if (workflowTemplate == null) { throw new WorkflowTemplateNotFoundException("Failed to scan workflow " + request.WorkflowId + " workflow template " + workflow.WorkflowTemplateId + "."); } //Get all the steps related to this task var workflowSteps = (await _entitiesRepository.GetAsync <Step>(s => s.WorkflowId == request.WorkflowId)).ToList(); foreach (var workflowStep in workflowSteps) { workflowStep.Outputs = DynamicDataUtility.DecryptDynamicData((await _entitiesRepository.GetFirstOrDefaultAsync <StepTemplate>(st => st.ReferenceId == workflowStep.StepTemplateId)).OutputDefinitions, workflowStep.Outputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey()); if (!workflowStep.IsComplete()) { messages.Add("Workflow step " + workflowStep.Id + " (" + workflowStep.Name + ")" + " is running."); workflowStillRunning = true; } } bool stepCreated = false; if (!workflowStillRunning) { if (workflow.CompletedLogicBlocks == null) { workflow.CompletedLogicBlocks = new List <string>(); } //Evaluate all logic blocks that have not been completed var logicBlocks = workflowTemplate.LogicBlocks.Where(lb => !workflow.CompletedLogicBlocks.Contains(lb.Key)).ToList(); foreach (var logicBlock in logicBlocks) { var lockId = Guid.NewGuid(); bool lockObtained = false; while (!lockObtained) { while (_clusterStateService.IsLogicBlockLocked(request.WorkflowId, logicBlock.Key)) { Console.WriteLine("Found " + ("workflow:" + request.WorkflowId + ">logicBlock:" + logicBlock) + " Lock"); await Task.Delay(1000); } int entryNumber = await _clusterStateService.LockLogicBlock(lockId, request.WorkflowId, logicBlock.Key); //Check whether you got the lock lockObtained = _clusterStateService.WasLockObtained(lockId, request.WorkflowId, logicBlock.Key); } //When the logic block is released, recheck whether this logic block has been evaluated workflow = await _entitiesRepository.GetFirstOrDefaultAsync <Workflow>(w => w.Id == request.WorkflowId); workflow.Inputs = DynamicDataUtility.DecryptDynamicData(workflowTemplate.InputDefinitions, workflow.Inputs, EncryptionProtocol.AES256, ClusterStateService.GetEncryptionKey()); if (workflow.CompletedLogicBlocks == null) { workflow.CompletedLogicBlocks = new List <string>(); } //If the logic block is ready to be processed, submit the steps if (logicBlock.Value.Dependencies.Evaluate(workflowSteps) && !workflow.CompletedLogicBlocks.Contains(logicBlock.Key)) { foreach (var substep in logicBlock.Value.SubsequentSteps) { if (workflowSteps.Where(s => s.Name == substep.Key).Count() == 0) { var verifiedInputs = new Dictionary <string, object>(); foreach (var mapping in substep.Value.Mappings) { //find the mapping with the highest priority var highestPriorityReference = WorkflowTemplateUtility.GetHighestPriorityReference(mapping.Value.OutputReferences, workflowSteps.ToArray()); //if the highest priority reference is null, there are no mappings if (highestPriorityReference == null && mapping.Value.DefaultValue == null) { } // If default value is higher priority else if (mapping.Value.DefaultValue != null && (highestPriorityReference == null || highestPriorityReference.Priority < mapping.Value.DefaultValue.Priority)) { verifiedInputs.Add(mapping.Key, mapping.Value.DefaultValue.Value); } // If the step ref is not -1 it is a step in the array but the workflow else if (highestPriorityReference.StepName != ReservedValues.WorkflowStartStepName) { var parentStep = workflowSteps.Where(ss => ss.Name == highestPriorityReference.StepName).FirstOrDefault(); //If there is a AND and there is no parent step, throw a error if (parentStep == null) { throw new InvalidWorkflowProcessingException("Missing source for mapping " + mapping.Key + " from step " + highestPriorityReference.StepName); } else { if (parentStep != null) { try { // Get the output value based on the pre-requisite id var outPutValue = DynamicDataUtility.GetData(parentStep.Outputs, highestPriorityReference.OutputId); // Validate it is in the definition verifiedInputs.Add(mapping.Key, outPutValue.Value); } catch (Exception e) { //TO DO Move this to logger Console.WriteLine("Found error at mapping " + mapping.Key + " for step " + substep.Key); throw e; } } } } //Get the value from the workflow ref else { // Validate it is in the definition verifiedInputs.Add(mapping.Key, DynamicDataUtility.GetData(workflow.Inputs, highestPriorityReference.OutputId).Value); } } stepCreated = true; //Add the step TODO, add step priority await _mediator.Send(new CreateStepCommand() { StepTemplateId = substep.Value.StepTemplateId, CreatedBy = SystemUsers.QUEUE_MANAGER, Inputs = verifiedInputs, WorkflowId = workflow.Id, Name = substep.Key //create the step with the right subsequent step }); messages.Add("Started workflow step " + substep.Key); } } //Mark it as evaluated workflow.UpdateJournal( new JournalEntry() { CreatedBy = request.CreatedBy, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "completedlogicblocks", Type = UpdateType.Append, Value = logicBlock.Key } } }); //await _workflowsRepository.UpdateWorkflow(workflow); await _node.Handle(new AddShardWriteOperation() { Data = workflow, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update }); } await _clusterStateService.UnlockLogicBlock(lockId, request.WorkflowId, logicBlock.Key); } //Check if there are no longer any steps that are unassigned or assigned var workflowStatus = workflow.Status; workflowSteps = (await _entitiesRepository.GetAsync <Step>(s => s.WorkflowId == request.WorkflowId)).ToList(); var highestStatus = StepStatuses.GetHighestPriority(workflowSteps.Select(s => s.Status).ToArray()); var newWorkflowStatus = stepCreated ? WorkflowStatuses.ConvertStepStatusToWorkflowStatus(StepStatuses.Unassigned) : WorkflowStatuses.ConvertStepStatusToWorkflowStatus(highestStatus); if (newWorkflowStatus != workflow.Status) { workflow.UpdateJournal( new JournalEntry() { CreatedBy = request.CreatedBy, CreatedOn = DateTime.UtcNow, Updates = new List <Update>() { new Update() { FieldName = "status", Type = UpdateType.Override, Value = newWorkflowStatus } } }); //await _workflowsRepository.UpdateWorkflow(workflow); await _node.Handle(new AddShardWriteOperation() { Data = workflow, WaitForSafeWrite = true, Operation = ConsensusCore.Domain.Enums.ShardOperationOptions.Update }); messages.Add("Updated workflow status " + newWorkflowStatus + "."); } } return(new CommandResult() { ObjectRefId = request.WorkflowId.ToString(), ElapsedMs = stopwatch.ElapsedMilliseconds, Type = CommandResultTypes.Update, IsSuccessful = true, Messages = messages.ToArray() }); }