/// <summary> /// Stash a given credentials object in the preferred /// secure storage, and return the Guid of the stashed object. /// </summary> /// <param name="token">AccessTokenCredential to stash</param> /// <param name="expiry">When the credentials expire, if unused</param> /// <returns>Guid of stashed object</returns> public async Task <Guid> StashCredentialAsync( AccessTokenCredential token, DateTimeOffset expiry = default) { if (expiry == default) { expiry = DateTimeOffset.Now.AddDays(7); } return(await _secureStorage.Persist(expiry, token)); }
/// <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); } }
/// <summary> /// Enumerate potential candidates for a given Provider module using /// a given AccessTokenCredential /// </summary> /// <param name="credential">Credentials to access cloud service to enumerate</param> /// <param name="provider">Provider to enumerate</param> /// <returns>Collection of suggestions of Provider configurations based on existing services</returns> public async Task <IEnumerable <ProviderResourceSuggestion> > EnumerateProviders(AccessTokenCredential credential, IAuthJanitorProvider provider) { provider.Credential = credential; if (provider is ICanEnumerateResourceCandidates) { var enumerable = provider as ICanEnumerateResourceCandidates; try { var results = await enumerable.EnumerateResourceCandidates(GetProviderConfiguration(provider.GetType().AssemblyQualifiedName, provider.SerializedConfiguration)); return(results.Select(r => new ProviderResourceSuggestion() { Name = r.Name, ProviderType = r.ProviderType, Configuration = r.Configuration, SerializedConfiguration = JsonSerializer.Serialize <object>(r.Configuration), AddressableNames = r.AddressableNames.Distinct(), ResourceValues = r.ResourceValues.Distinct(), ResourcesAddressingThis = r.ResourcesAddressingThis })); } catch (Exception ex) { _serviceProvider.GetRequiredService <ILogger <ProviderManagerService> >().LogError(ex, "Error enumerating resource candidates for provider type " + provider.GetType().AssemblyQualifiedName); } } return(new ProviderResourceSuggestion[0]); }
public async Task <AccessTokenCredential> GetTokenCredentialAsync(Guid taskId, CancellationToken cancellationToken) { var task = await _rekeyingTasks.GetOne(taskId, cancellationToken); // Retrieve credentials for Task AccessTokenCredential credential = null; try { if (task.ConfirmationType == TaskConfirmationStrategies.AdminCachesSignOff) { if (task.PersistedCredentialId == default) { throw new KeyNotFoundException("Cached sign-off is preferred but no credentials were persisted!"); } if (_secureStorageProvider == null) { throw new NotSupportedException("Must register an ISecureStorageProvider"); } credential = await _secureStorageProvider.Retrieve <AccessTokenCredential>(task.PersistedCredentialId); } else if (task.ConfirmationType == TaskConfirmationStrategies.AdminSignsOffJustInTime) { credential = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync(); } else if (task.ConfirmationType.UsesServicePrincipal()) { credential = await _identityService.GetAccessTokenForApplicationAsync(); } else { throw new NotSupportedException("No Access Tokens could be generated for this Task!"); } if (credential == null || string.IsNullOrEmpty(credential.AccessToken)) { throw new InvalidOperationException("Access Token was found, but was blank or invalid"); } credential.DisplayUserName = credential.Username; credential.DisplayEmail = credential.Username; if (task.ConfirmationType.UsesOBOTokens()) { if (!string.IsNullOrEmpty(task.PersistedCredentialUser)) { credential.DisplayUserName = task.PersistedCredentialUser; } else { credential.DisplayUserName = _identityService.UserName; credential.DisplayEmail = _identityService.UserEmail; } } return(credential); } catch (Exception ex) { await _eventDispatcherService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task); throw ex; } }
/// <summary> /// Enumerate potential candidates for all loaded Provider modules using /// a given AccessTokenCredential /// </summary> /// <param name="credential">Credentials to access cloud service to enumerate</param> /// <returns>Collection of suggestions of Provider configurations based on existing services</returns> public async Task <IEnumerable <ProviderResourceSuggestion> > EnumerateProviders(AccessTokenCredential credential) { var providers = (await Task.WhenAll( LoadedProviders.Select(p => GetProviderInstanceDefault(p.ProviderTypeName)) .OfType <ICanEnumerateResourceCandidates>() .Select(p => EnumerateProviders(credential, p)))) .Where(c => c != null) .SelectMany(c => c); foreach (var provider in providers.Where(p => p.AddressableNames.Any())) { foreach (var name in provider.AddressableNames) { var refs = providers.Where(p => p.ResourceValues.Any(r => r.Contains(name))); provider.ResourcesAddressingThis.AddRange(refs); } } return(providers); }