Пример #1
0
        public async Task ExecuteTask(Guid taskId, CancellationToken cancellationToken)
        {
            // Prepare record
            _logger.LogInformation("Retrieving task {TaskId}", taskId);
            var task = await _rekeyingTasks.GetOne(taskId, cancellationToken);

            task.RekeyingInProgress = true;

            // Create task to perform regular updates to UI (every 15s)
            _logger.LogInformation("Starting log update task");
            var logUpdateCancellationTokenSource = new CancellationTokenSource();
            var logUpdateTask = Task.Run(async() =>
            {
                while (task.RekeyingInProgress)
                {
                    await Task.Delay(15 * 1000);
                    await _rekeyingTasks.Update(task, cancellationToken);
                }
            }, logUpdateCancellationTokenSource.Token);

            // Retrieve the secret configuration and its resources
            var secret = await _managedSecrets.GetOne(task.ManagedSecretId, cancellationToken);

            _logger.LogInformation("Retrieving resources for secret {SecretId}", secret.ObjectId);
            var resources = await _resources.Get(r => secret.ResourceIds.Contains(r.ObjectId), cancellationToken);

            ProviderWorkflowActionCollection workflowCollection = null;

            try
            {
                // Create configured providers for each resource
                _logger.LogInformation("Getting providers for {ResourceCount} resources", resources.Count);
                var providers = resources.Select(r => _providerManagerService.GetProviderInstance(
                                                     r.ProviderType,
                                                     r.ProviderConfiguration)).ToList();

                // Generate a workflow collection from the configured providers
                _logger.LogInformation("Creating workflow collection");
                workflowCollection = _providerManagerService.CreateWorkflowCollection(
                    secret.ValidPeriod,
                    providers);

                // Get the token credential for this task and embed it
                _logger.LogInformation("Embedding credentials");
                workflowCollection.EmbedCredentials(await GetTokenCredentialAsync(taskId, cancellationToken));

                // TODO: Per-action credentialing will go here eventually
                // For now, use the snippet above for all instances
                //workflowCollection.Actions.ToList().ForEach(a =>
                //{
                //    a.Instance.Credential = credential;
                //});

                try
                {
                    // Update the UI with the data from this attempt
                    _logger.LogInformation("Adding workflow collection to task");
                    task.Attempts.Add(workflowCollection);
                    await _rekeyingTasks.Update(task, cancellationToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error updating task: {ex}", ex.ToString());
                    throw ex;
                }


                // Execute the workflow collection
                _logger.LogInformation("Executing {ActionCount} actions", workflowCollection.Actions.Count);
                try { await workflowCollection.Run(); }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error executing action(s)");
                    await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error preparing workflow: {ex}", ex);
                await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task);
            }

            // Update Task record
            _logger.LogInformation("Completing task record");
            task.RekeyingInProgress = false;
            task.RekeyingCompleted  = (workflowCollection?.HasBeenExecuted).GetValueOrDefault();
            task.RekeyingCompleted  = (workflowCollection?.HasBeenExecutedSuccessfully).GetValueOrDefault();

            // End the regular update task and perform one final data update with the results
            logUpdateCancellationTokenSource.Cancel();
            await _rekeyingTasks.Update(task, cancellationToken);

            // Run cleanup if Task is complete
            if (!task.RekeyingFailed)
            {
                _logger.LogInformation("Performing cleanup");
                try
                {
                    secret.LastChanged = DateTimeOffset.UtcNow;
                    if (task.PersistedCredentialId != default && task.PersistedCredentialId != Guid.Empty)
                    {
                        await _secureStorageProvider.Destroy(task.PersistedCredentialId);

                        task.PersistedCredentialId   = default;
                        task.PersistedCredentialUser = default;
                    }

                    await _rekeyingTasks.Update(task, cancellationToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error deleting persisted credentials");
                    await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(TaskExecutionMetaService.ExecuteTask),
                                                                    "Failure to clean up persisted credentials");
                }

                if (task.ConfirmationType.UsesOBOTokens())
                {
                    await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCompletedManually, nameof(TaskExecutionMetaService.ExecuteTask), task);
                }
                else
                {
                    await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCompletedAutomatically, nameof(TaskExecutionMetaService.ExecuteTask), task);
                }
            }
            else
            {
                await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task);
            }
        }
Пример #2
0
        /// <summary>
        /// Process a given set of providerConfigurations and execute the
        /// workflow locally. If the Instance ID doesn't match the expected
        /// Agent ID, this will return immediately.
        ///
        /// The periodicUpdateFunction will be executed regularly throughout
        /// the execution of the workflow.
        /// </summary>
        /// <param name="secretValidPeriod">Secret's period of validity</param>
        /// <param name="periodicUpdateFunction">Function which is invoked periodically to communicate runtime status</param>
        /// <param name="providerConfigurations">Provider configurations</param>
        /// <returns></returns>
        public async Task <ProviderWorkflowActionCollection> ExecuteAsync(
            TimeSpan secretValidPeriod,
            Func <ProviderWorkflowActionCollection, Task> periodicUpdateFunction,
            params ProviderExecutionParameters[] providerConfigurations)
        {
            ProviderWorkflowActionCollection workflowCollection =
                new ProviderWorkflowActionCollection();

            try
            {
                var persisted = new Dictionary <Guid, AccessTokenCredential>();
                if (providerConfigurations.Any(p => p.TokenSource == TokenSources.Persisted))
                {
                    _logger.LogInformation("Downloading persisted tokens");
                    foreach (var item in providerConfigurations.Where(p => p.TokenSource == TokenSources.Persisted))
                    {
                        var guid = Guid.Parse(item.TokenParameter);
                        persisted[guid] = await _secureStorage.Retrieve <AccessTokenCredential>(guid);
                    }
                }

                AccessTokenCredential obo = null, msi = null;
                if (providerConfigurations.Any(p => p.TokenSource == TokenSources.OBO))
                {
                    _logger.LogInformation("Acquiring OBO token");
                    obo = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync();
                }
                if (providerConfigurations.Any(p => p.TokenSource == TokenSources.ServicePrincipal))
                {
                    _logger.LogInformation("Acquiring application token");
                    msi = await _identityService.GetAccessTokenForApplicationAsync();
                }

                _logger.LogInformation("Getting providers for {ResourceCount} resources", providerConfigurations.Count());
                await Task.WhenAll(providerConfigurations.Select(async r =>
                {
                    switch (r.TokenSource)
                    {
                    case TokenSources.Explicit:
                        r.AccessToken = JsonConvert.DeserializeObject <AccessTokenCredential>(r.TokenParameter);
                        break;

                    case TokenSources.OBO:
                        r.AccessToken = obo;
                        r.AccessToken.DisplayEmail    = _identityService.UserEmail;
                        r.AccessToken.DisplayUserName = _identityService.UserName;
                        break;

                    case TokenSources.Persisted:
                        r.AccessToken = persisted[Guid.Parse(r.TokenParameter)];
                        r.AccessToken.DisplayEmail    = r.AccessToken.Username;
                        r.AccessToken.DisplayUserName = r.AccessToken.Username;
                        break;

                    case TokenSources.ServicePrincipal:
                        r.AccessToken = msi;
                        break;

                    case TokenSources.Unknown:
                    default:
                        await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred,
                                                             nameof(AuthJanitorService.ExecuteAsync),
                                                             $"TokenSource was unknown for a provider! ({r.ProviderType})");
                        break;
                    }
                    return(r);
                }));

                // --- end access token acquisition/embed ---

                var providers = providerConfigurations.Select(r =>
                {
                    var p = _providerManagerService.GetProviderInstance(
                        r.ProviderType,
                        r.ProviderConfiguration);
                    if (r.AccessToken != null)
                    {
                        p.Credential = r.AccessToken;
                    }
                    return(p);
                }).ToList();

                workflowCollection = _providerManagerService.CreateWorkflowCollection(
                    secretValidPeriod,
                    providers);

                _logger.LogInformation("Creating workflow execution task");
                Task workflowCollectionRunTask = new Task(async() =>
                {
                    try { await workflowCollection.Run(); }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error executing action(s)");
                        throw ex;
                    }
                });

                _logger.LogInformation("Creating tracker task for workflow collection");
                var logUpdateCancellationTokenSource = new CancellationTokenSource();
                var periodicUpdateTask = Task.Run(async() =>
                {
                    while (!workflowCollectionRunTask.IsCompleted &&
                           !workflowCollectionRunTask.IsCanceled)
                    {
                        await Task.Delay(5 * 1000);
                        await periodicUpdateFunction(workflowCollection);
                    }
                }, logUpdateCancellationTokenSource.Token);

                _logger.LogInformation("Executing {ActionCount} actions", workflowCollection.Actions.Count);
                await workflowCollectionRunTask;
                _logger.LogInformation("Execution complete", workflowCollection.Actions.Count);

                logUpdateCancellationTokenSource.Cancel();
                await periodicUpdateFunction(workflowCollection);

                if (workflowCollection.HasBeenExecutedSuccessfully)
                {
                    if (providerConfigurations.Any(p => p.TokenSource == TokenSources.Persisted))
                    {
                        _logger.LogInformation("Cleaning up persisted tokens");
                        foreach (var item in providerConfigurations.Where(p => p.TokenSource == TokenSources.Persisted))
                        {
                            await _secureStorage.Destroy(Guid.Parse(item.TokenParameter));
                        }
                    }
                }

                return(workflowCollection);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error preparing workflow: {ex}", ex);
                await _eventDispatcher.DispatchEvent(
                    AuthJanitorSystemEvents.RotationTaskAttemptFailed,
                    nameof(AuthJanitorService.ExecuteAsync),
                    "Error executing provider workflow");

                return(workflowCollection);
            }
        }
Пример #3
0
        /// <summary>
        /// Create a Workflow Collection based on actions which need to
        /// be taken to execute a given set of Providers. This includes
        /// proper ordering of actions as required.
        /// </summary>
        /// <param name="validPeriod">Valid period for secret</param>
        /// <param name="providers">Providers to generate workflow collection from</param>
        /// <returns>Workflow collection</returns>
        public ProviderWorkflowActionCollection CreateWorkflowCollection(
            TimeSpan validPeriod,
            IEnumerable <IAuthJanitorProvider> providers)
        {
            var workflowCollection = new ProviderWorkflowActionCollection(_serviceProvider);

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanRunSanityTests>()
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Sanity Test",
                                                               p, p => p.Test())).ToArray());

            // ---

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanGenerateTemporarySecretValue>()
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Generate Temporary Secrets",
                                                               p, p => p.GenerateTemporarySecretValue())).ToArray());

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanDistributeTemporarySecretValues>()
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Distribute Temporary Secrets",
                                                               p, p =>
            {
                var actions = workflowCollection.GetActions <ICanGenerateTemporarySecretValue, RegeneratedSecret>();
                if (actions.Any())
                {
                    return(p.DistributeTemporarySecretValues(actions.Select(a => a.Result).ToList()));
                }
                return(Task.FromResult(false));
            })).ToArray());

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanPerformUnifiedCommitForTemporarySecretValues>()
                                                   .GroupBy(p => p.GenerateResourceIdentifierHashCode())
                                                   .Select(p => p.First())
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Perform Unified Commit",
                                                               p, p => p.UnifiedCommitForTemporarySecretValues())).ToArray());

            // ---

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanRekey>()
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Rekey Object",
                                                               p, p => p.Rekey(validPeriod))).ToArray());

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanDistributeLongTermSecretValues>()
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Distribute Rekeyed Secrets",
                                                               p, p =>
            {
                return(p.DistributeLongTermSecretValues(
                           workflowCollection.GetActions <ICanRekey, RegeneratedSecret>()
                           .Select(a => a.Result).ToList()));
            })).ToArray());

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanPerformUnifiedCommit>()
                                                   .GroupBy(p => p.GenerateResourceIdentifierHashCode())
                                                   .Select(p => p.First())
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Perform Unified Commit on Rekeyed Secrets",
                                                               p, p => p.UnifiedCommit())).ToArray());

            // ---

            workflowCollection.AddWithOneIncrement(providers
                                                   .OfType <ICanCleanup>()
                                                   .Select(DuplicateProvider)
                                                   .Select(p => ProviderWorkflowAction.Create(
                                                               "Cleanup",
                                                               p, p => p.Cleanup())).ToArray());

            return(workflowCollection);
        }