Agent messaging and execution abstraction (#107)
* Agent messaging and execution abstraction Centralize functions to AuthJanitorService, which operates on other loaded services * Formatting cleanup and license headers * Documentation * Finish splitting Data Data is now an operationally separate entity which exists only for the sake of management convenience
This commit is contained in:
Родитель
92368a291a
Коммит
08d683afd0
|
@ -78,9 +78,6 @@
|
|||
case AuthJanitor.Integrity.IntegrityReportExtensibilityType.ExtensibilityTypes.CryptographicImplementation:
|
||||
iconName = FontAwesomeIcons.Lock;
|
||||
break;
|
||||
case AuthJanitor.Integrity.IntegrityReportExtensibilityType.ExtensibilityTypes.DataStore:
|
||||
iconName = FontAwesomeIcons.Database;
|
||||
break;
|
||||
case AuthJanitor.Integrity.IntegrityReportExtensibilityType.ExtensibilityTypes.EventSink:
|
||||
iconName = FontAwesomeIcons.Bell;
|
||||
break;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright (c) Microsoft Corporation. -->
|
||||
<!-- Licensed under the MIT license. -->
|
||||
<html>
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrity;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace AuthJanitor.UI.Shared
|
||||
{
|
||||
public static class AuthJanitorServiceRegistration
|
||||
{
|
||||
public static void RegisterServices(IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddSingleton<EventDispatcherMetaService>();
|
||||
serviceCollection.AddSingleton<TaskExecutionMetaService>();
|
||||
serviceCollection.AddSingleton<SystemIntegrityService>();
|
||||
|
||||
serviceCollection.AddTransient<ProviderWorkflowActionLogger>();
|
||||
serviceCollection.AddTransient(typeof(ProviderWorkflowActionLogger<>), typeof(ProviderWorkflowActionLogger<>));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.DataStores;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Integrations.DataStores
|
||||
namespace AuthJanitor.DataStores
|
||||
{
|
||||
public interface IDataStore<TStoredModel> : IAuthJanitorExtensibilityPoint
|
||||
public interface IDataStore<TStoredModel>
|
||||
where TStoredModel : IAuthJanitorModel
|
||||
{
|
||||
/// <summary>
|
|
@ -14,6 +14,7 @@ namespace AuthJanitor.UI.Shared.Models
|
|||
public string Description { get; set; }
|
||||
|
||||
public TaskConfirmationStrategies TaskConfirmationStrategies { get; set; } = TaskConfirmationStrategies.None;
|
||||
public string ExecutingAgentId { get; set; } = "admin-service";
|
||||
|
||||
public DateTimeOffset LastChanged { get; set; } = DateTimeOffset.MinValue;
|
||||
public TimeSpan ValidPeriod { get; set; }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the MIT license.
|
||||
using System;
|
||||
|
||||
namespace AuthJanitor.UI.Shared
|
||||
namespace AuthJanitor
|
||||
{
|
||||
[Serializable]
|
||||
[Flags]
|
||||
|
|
|
@ -1,250 +1,214 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.SecureStorage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AuthJanitor.UI.Shared.MetaServices
|
||||
{
|
||||
public class TaskExecutionMetaService
|
||||
{
|
||||
private readonly IDataStore<ManagedSecret> _managedSecrets;
|
||||
private readonly IDataStore<RekeyingTask> _rekeyingTasks;
|
||||
private readonly IDataStore<Resource> _resources;
|
||||
private readonly ISecureStorage _secureStorageProvider;
|
||||
|
||||
private readonly ILogger<TaskExecutionMetaService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ProviderManagerService _providerManagerService;
|
||||
|
||||
private readonly EventDispatcherMetaService _eventDispatcherMetaService;
|
||||
private readonly IIdentityService _identityService;
|
||||
|
||||
public TaskExecutionMetaService(
|
||||
ILogger<TaskExecutionMetaService> logger,
|
||||
IServiceProvider serviceProvider,
|
||||
EventDispatcherMetaService eventDispatcherMetaService,
|
||||
IIdentityService identityService,
|
||||
ProviderManagerService providerManagerService,
|
||||
IDataStore<ManagedSecret> managedSecrets,
|
||||
IDataStore<RekeyingTask> rekeyingTasks,
|
||||
IDataStore<Resource> resources,
|
||||
ISecureStorage secureStorageProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
_eventDispatcherMetaService = eventDispatcherMetaService;
|
||||
_identityService = identityService;
|
||||
_providerManagerService = providerManagerService;
|
||||
_managedSecrets = managedSecrets;
|
||||
_rekeyingTasks = rekeyingTasks;
|
||||
_resources = resources;
|
||||
_secureStorageProvider = secureStorageProvider;
|
||||
}
|
||||
|
||||
public async Task CacheBackCredentialsForTaskIdAsync(Guid taskId, CancellationToken cancellationToken)
|
||||
{
|
||||
var task = await _rekeyingTasks.GetOne(taskId, cancellationToken);
|
||||
if (task == null)
|
||||
throw new KeyNotFoundException("Task not found");
|
||||
|
||||
if (task.ConfirmationType != TaskConfirmationStrategies.AdminCachesSignOff)
|
||||
throw new InvalidOperationException("Task does not persist credentials");
|
||||
|
||||
if (_secureStorageProvider == null)
|
||||
throw new NotSupportedException("Must register an ISecureStorageProvider");
|
||||
|
||||
var credentialId = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync()
|
||||
.ContinueWith(t => _secureStorageProvider.Persist(task.Expiry, t.Result))
|
||||
.Unwrap();
|
||||
|
||||
task.PersistedCredentialId = credentialId;
|
||||
task.PersistedCredentialUser = _identityService.UserName;
|
||||
|
||||
await _rekeyingTasks.Update(task, cancellationToken);
|
||||
}
|
||||
|
||||
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 _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private TProvider DuplicateProvider<TProvider>(TProvider provider)
|
||||
where TProvider : IAuthJanitorProvider => _providerManagerService.GetProviderInstance(provider);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.SecureStorage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using AuthJanitor.DataStores;
|
||||
|
||||
namespace AuthJanitor
|
||||
{
|
||||
public class TaskExecutionMetaService
|
||||
{
|
||||
private readonly IDataStore<ManagedSecret> _managedSecrets;
|
||||
private readonly IDataStore<RekeyingTask> _rekeyingTasks;
|
||||
private readonly IDataStore<Resource> _resources;
|
||||
private readonly ISecureStorage _secureStorageProvider;
|
||||
|
||||
private readonly ILogger<TaskExecutionMetaService> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ProviderManagerService _providerManagerService;
|
||||
|
||||
private readonly EventDispatcherService _eventDispatcherService;
|
||||
private readonly IIdentityService _identityService;
|
||||
|
||||
private readonly AuthJanitorService _authJanitorService;
|
||||
|
||||
public TaskExecutionMetaService(
|
||||
ILogger<TaskExecutionMetaService> logger,
|
||||
IServiceProvider serviceProvider,
|
||||
EventDispatcherService eventDispatcherService,
|
||||
IIdentityService identityService,
|
||||
ProviderManagerService providerManagerService,
|
||||
IDataStore<ManagedSecret> managedSecrets,
|
||||
IDataStore<RekeyingTask> rekeyingTasks,
|
||||
IDataStore<Resource> resources,
|
||||
ISecureStorage secureStorageProvider,
|
||||
AuthJanitorService authJanitorService)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
_eventDispatcherService = eventDispatcherService;
|
||||
_identityService = identityService;
|
||||
_providerManagerService = providerManagerService;
|
||||
_managedSecrets = managedSecrets;
|
||||
_rekeyingTasks = rekeyingTasks;
|
||||
_resources = resources;
|
||||
_secureStorageProvider = secureStorageProvider;
|
||||
_authJanitorService = authJanitorService;
|
||||
}
|
||||
|
||||
public async Task CacheBackCredentialsForTaskIdAsync(Guid taskId, CancellationToken cancellationToken)
|
||||
{
|
||||
var task = await _rekeyingTasks.GetOne(taskId, cancellationToken);
|
||||
if (task == null)
|
||||
throw new KeyNotFoundException("Task not found");
|
||||
|
||||
if (task.ConfirmationType != TaskConfirmationStrategies.AdminCachesSignOff)
|
||||
throw new InvalidOperationException("Task does not persist credentials");
|
||||
|
||||
if (_secureStorageProvider == null)
|
||||
throw new NotSupportedException("Must register an ISecureStorageProvider");
|
||||
|
||||
var credentialId = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync()
|
||||
.ContinueWith(t => _secureStorageProvider.Persist(task.Expiry, t.Result))
|
||||
.Unwrap();
|
||||
|
||||
task.PersistedCredentialId = credentialId;
|
||||
task.PersistedCredentialUser = _identityService.UserName;
|
||||
|
||||
await _rekeyingTasks.Update(task, cancellationToken);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private TProvider DuplicateProvider<TProvider>(TProvider provider)
|
||||
where TProvider : IAuthJanitorProvider => _providerManagerService.GetProviderInstance(provider);
|
||||
|
||||
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);
|
||||
|
||||
var workflowCollection = await _authJanitorService.ExecuteAsync(
|
||||
secret.ValidPeriod,
|
||||
async (pwac) =>
|
||||
{
|
||||
if (!task.Attempts.Any(a => a.StartedExecution == pwac.StartedExecution))
|
||||
{
|
||||
task.Attempts.Add(pwac);
|
||||
await _rekeyingTasks.Update(task, cancellationToken);
|
||||
}
|
||||
},
|
||||
resources.Select(r =>
|
||||
{
|
||||
TokenSources tokenSource = TokenSources.Unknown;
|
||||
string tokenParameter = string.Empty;
|
||||
switch (secret.TaskConfirmationStrategies)
|
||||
{
|
||||
case TaskConfirmationStrategies.AdminSignsOffJustInTime:
|
||||
tokenSource = TokenSources.OBO;
|
||||
break;
|
||||
case TaskConfirmationStrategies.AdminCachesSignOff:
|
||||
tokenSource = TokenSources.Persisted;
|
||||
tokenParameter = task.PersistedCredentialId.ToString();
|
||||
break;
|
||||
case TaskConfirmationStrategies.AutomaticRekeyingAsNeeded:
|
||||
case TaskConfirmationStrategies.AutomaticRekeyingScheduled:
|
||||
case TaskConfirmationStrategies.ExternalSignal:
|
||||
tokenSource = TokenSources.ServicePrincipal;
|
||||
break;
|
||||
}
|
||||
return new ProviderExecutionParameters()
|
||||
{
|
||||
ProviderType = r.ProviderType,
|
||||
ProviderConfiguration = r.ProviderConfiguration,
|
||||
AgentId = secret.ExecutingAgentId,
|
||||
TokenSource = tokenSource,
|
||||
TokenParameter = tokenParameter
|
||||
};
|
||||
}).ToArray());
|
||||
|
||||
// Update Task record
|
||||
_logger.LogInformation("Completing task record");
|
||||
task.RekeyingInProgress = false;
|
||||
task.RekeyingCompleted = (workflowCollection?.HasBeenExecuted).GetValueOrDefault();
|
||||
task.RekeyingCompleted = (workflowCollection?.HasBeenExecutedSuccessfully).GetValueOrDefault();
|
||||
await _rekeyingTasks.Update(task, cancellationToken);
|
||||
|
||||
if (workflowCollection.HasBeenExecutedSuccessfully)
|
||||
{
|
||||
if (task.ConfirmationType.UsesOBOTokens())
|
||||
await _eventDispatcherService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCompletedManually, nameof(TaskExecutionMetaService.ExecuteTask), task);
|
||||
else
|
||||
await _eventDispatcherService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCompletedAutomatically, nameof(TaskExecutionMetaService.ExecuteTask), task);
|
||||
}
|
||||
else
|
||||
await _eventDispatcherService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskAttemptFailed, nameof(TaskExecutionMetaService.ExecuteTask), task);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the MIT license.
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.UI.Shared.ViewModels;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
@ -14,6 +13,7 @@ using System.Reflection;
|
|||
using System.Threading;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using AuthJanitor.DataStores;
|
||||
|
||||
namespace AuthJanitor.UI.Shared
|
||||
{
|
||||
|
@ -143,6 +143,7 @@ namespace AuthJanitor.UI.Shared
|
|||
Name = secret.Name,
|
||||
Description = secret.Description,
|
||||
TaskConfirmationStrategies = secret.TaskConfirmationStrategies,
|
||||
ExecutingAgentId = secret.ExecutingAgentId,
|
||||
LastChanged = secret.LastChanged,
|
||||
ValidPeriodMinutes = (int)secret.ValidPeriod.TotalMinutes,
|
||||
Nonce = secret.Nonce,
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace AuthJanitor.UI.Shared.ViewModels
|
|||
}
|
||||
|
||||
public int TaskConfirmationStrategiesInt { get; set; }
|
||||
public string ExecutingAgentId { get; set; }
|
||||
|
||||
public DateTimeOffset? LastChanged { get; set; }
|
||||
public int ValidPeriodMinutes { get; set; }
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Agents
|
||||
{
|
||||
public class AgentMessageEnvelope
|
||||
{
|
||||
public DateTimeOffset Created { get; set; }
|
||||
|
||||
public string Originator { get; set; }
|
||||
public string Target { get; set; }
|
||||
|
||||
public string MessageType { get; set; }
|
||||
public byte[] Message { get; set; } = new byte[0];
|
||||
public IAgentMessage MessageObject { get; set; } = null;
|
||||
|
||||
public byte[] Signature { get; set; }
|
||||
|
||||
|
||||
public static async Task<AgentMessageEnvelope> Create(
|
||||
ICryptographicImplementation cryptographicImplementation,
|
||||
string originator,
|
||||
string target,
|
||||
IAgentMessage message)
|
||||
{
|
||||
var envelope = new AgentMessageEnvelope()
|
||||
{
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
Originator = originator,
|
||||
Target = target,
|
||||
MessageType = message.GetType().Name,
|
||||
Message = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message))
|
||||
};
|
||||
|
||||
envelope.Signature = await cryptographicImplementation.Sign(
|
||||
await GetMessageEnvelopeHash(cryptographicImplementation, envelope));
|
||||
|
||||
return envelope;
|
||||
}
|
||||
|
||||
public async Task<bool> VerifyAndUnpack(
|
||||
ICryptographicImplementation cryptographicImplementation)
|
||||
{
|
||||
MessageObject = null;
|
||||
if (!await Verify(cryptographicImplementation))
|
||||
return false;
|
||||
MessageObject = (IAgentMessage)JsonConvert.DeserializeObject(
|
||||
Encoding.UTF8.GetString(Message),
|
||||
Type.GetType(MessageType));
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> Verify(
|
||||
ICryptographicImplementation cryptographicImplementation) =>
|
||||
await cryptographicImplementation.Verify(
|
||||
await GetMessageEnvelopeHash(cryptographicImplementation, this),
|
||||
this.Signature);
|
||||
|
||||
private static Task<byte[]> GetMessageEnvelopeHash(
|
||||
ICryptographicImplementation cryptographicImplementation,
|
||||
AgentMessageEnvelope envelope) =>
|
||||
cryptographicImplementation.Hash(
|
||||
new byte[][] {
|
||||
BitConverter.GetBytes(envelope.Created.Ticks),
|
||||
Encoding.UTF8.GetBytes(envelope.Originator),
|
||||
Encoding.UTF8.GetBytes(envelope.Target),
|
||||
Encoding.UTF8.GetBytes(envelope.MessageType),
|
||||
envelope.Message
|
||||
}.SelectMany(a => a).ToArray());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Providers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AuthJanitor.Agents
|
||||
{
|
||||
public class AgentProviderCommandMessage : IAgentMessage
|
||||
{
|
||||
public List<ProviderExecutionParameters> Providers { get; set; } =
|
||||
new List<ProviderExecutionParameters>();
|
||||
|
||||
public TimeSpan ValidPeriod { get; set; }
|
||||
public string State { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Providers;
|
||||
|
||||
namespace AuthJanitor.Agents
|
||||
{
|
||||
public class AgentProviderStatusMessage : IAgentMessage
|
||||
{
|
||||
public string State { get; set; }
|
||||
|
||||
public ProviderWorkflowActionCollection WorkflowActionCollection { get; set; } =
|
||||
new ProviderWorkflowActionCollection();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using System;
|
||||
|
||||
namespace AuthJanitor.Agents
|
||||
{
|
||||
public class AgentStatusMessage : IAgentMessage
|
||||
{
|
||||
public DateTimeOffset CurrentTime { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Agents
|
||||
{
|
||||
public interface IAgentCommunicationProvider
|
||||
{
|
||||
Task Send(AgentMessageEnvelope envelop);
|
||||
Task<AgentMessageEnvelope> TryReceive();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
namespace AuthJanitor.Agents
|
||||
{
|
||||
public interface IAgentMessage
|
||||
{
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AuthenticodeExaminer" Version="0.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Text.Json" Version="4.7.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
|
||||
|
|
|
@ -0,0 +1,355 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Agents;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Integrity;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.SecureStorage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor
|
||||
{
|
||||
public class AuthJanitorServiceOptions
|
||||
{
|
||||
public string InstanceId { get; set; }
|
||||
}
|
||||
|
||||
public static class AuthJanitorServiceExtensions
|
||||
{
|
||||
public static void AddAuthJanitorService(this IServiceCollection serviceCollection,
|
||||
string instanceIdentity,
|
||||
Type[] providerTypes)
|
||||
{
|
||||
serviceCollection.AddSingleton<EventDispatcherService>();
|
||||
serviceCollection.AddSingleton<SystemIntegrityService>();
|
||||
|
||||
serviceCollection.AddSingleton<ProviderManagerService>((s) =>
|
||||
new ProviderManagerService(s, providerTypes));
|
||||
|
||||
serviceCollection.Configure<AuthJanitorServiceOptions>((o) => o.InstanceId = instanceIdentity);
|
||||
serviceCollection.AddSingleton<AuthJanitorService>();
|
||||
|
||||
serviceCollection.AddTransient<ProviderWorkflowActionLogger>();
|
||||
serviceCollection.AddTransient(typeof(ProviderWorkflowActionLogger<>), typeof(ProviderWorkflowActionLogger<>));
|
||||
}
|
||||
}
|
||||
|
||||
public class AuthJanitorService
|
||||
{
|
||||
private readonly ILogger<AuthJanitorService> _logger;
|
||||
private readonly ProviderManagerService _providerManagerService;
|
||||
private readonly ICryptographicImplementation _cryptographicImplementation;
|
||||
private readonly ISecureStorage _secureStorage;
|
||||
private readonly IIdentityService _identityService;
|
||||
private readonly EventDispatcherService _eventDispatcher;
|
||||
private readonly IAgentCommunicationProvider _agentCommunicationProvider;
|
||||
private readonly IOptions<AuthJanitorServiceOptions> _options;
|
||||
|
||||
public AuthJanitorService(
|
||||
ILogger<AuthJanitorService> logger,
|
||||
ProviderManagerService providerManagerService,
|
||||
ICryptographicImplementation cryptographicImplementation,
|
||||
ISecureStorage secureStorage,
|
||||
IIdentityService identityService,
|
||||
EventDispatcherService eventDispatcherService,
|
||||
IAgentCommunicationProvider agentCommunicationProvider,
|
||||
IOptions<AuthJanitorServiceOptions> options)
|
||||
{
|
||||
_logger = logger;
|
||||
_providerManagerService = providerManagerService;
|
||||
_cryptographicImplementation = cryptographicImplementation;
|
||||
_secureStorage = secureStorage;
|
||||
_identityService = identityService;
|
||||
_eventDispatcher = eventDispatcherService;
|
||||
_agentCommunicationProvider = agentCommunicationProvider;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process an incoming serialized message.
|
||||
///
|
||||
/// The message will be deserialized, its signature verified,
|
||||
/// and it will be routed.
|
||||
///
|
||||
/// If the message is a command to execute, the execution will
|
||||
/// run and the periodicUpdateFunction will be run regularly to
|
||||
/// update the caller on the runtime status.
|
||||
///
|
||||
/// If the message is a status update, the periodicUpdateFunction
|
||||
/// will be executed only once.
|
||||
/// </summary>
|
||||
/// <param name="serializedMessage">Serialized Agent message</param>
|
||||
/// <param name="periodicUpdateFunction">Function which is invoked periodically to communicate runtime status</param>
|
||||
/// <returns></returns>
|
||||
public async Task ProcessMessageAsync(
|
||||
string serializedMessage,
|
||||
Func<ProviderWorkflowActionCollection, Task> periodicUpdateFunction)
|
||||
{
|
||||
var envelope = JsonConvert.DeserializeObject<AgentMessageEnvelope>(serializedMessage);
|
||||
envelope.MessageObject = null;
|
||||
|
||||
if (envelope.Target != _options.Value.InstanceId)
|
||||
return;
|
||||
|
||||
if (!await envelope.VerifyAndUnpack(_cryptographicImplementation))
|
||||
{
|
||||
await _eventDispatcher.DispatchEvent(
|
||||
EventSinks.AuthJanitorSystemEvents.AnomalousEventOccurred,
|
||||
nameof(AuthJanitorService.ProcessMessageAsync),
|
||||
"Failed to verify agent message! This may indicate agent compromise.");
|
||||
throw new Exception("Message verification failed!");
|
||||
}
|
||||
|
||||
if (envelope.MessageObject is AgentProviderCommandMessage)
|
||||
{
|
||||
var message = envelope.MessageObject as AgentProviderCommandMessage;
|
||||
await ExecuteAsync(message.ValidPeriod,
|
||||
periodicUpdateFunction,
|
||||
message.Providers.ToArray());
|
||||
}
|
||||
if (envelope.MessageObject is AgentProviderStatusMessage)
|
||||
{
|
||||
var message = envelope.MessageObject as AgentProviderStatusMessage;
|
||||
var taskId = Guid.Parse(message.State);
|
||||
await periodicUpdateFunction(message.WorkflowActionCollection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stash the credentials for the current user in the preferred
|
||||
/// secure storage, and return the Guid of the stashed object.
|
||||
/// </summary>
|
||||
/// <param name="expiry">When the credentials expire, if unused</param>
|
||||
/// <returns>Guid of stashed object</returns>
|
||||
public async Task<Guid> StashCredentialForCurrentUserAsync(DateTimeOffset expiry = default) =>
|
||||
await StashCredentialAsync(
|
||||
await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync(),
|
||||
expiry);
|
||||
|
||||
/// <summary>
|
||||
/// Stash the credentials for the application's identity in the preferred
|
||||
/// secure storage, and return the Guid of the stashed object.
|
||||
/// </summary>
|
||||
/// <param name="expiry">When the credentials expire, if unused</param>
|
||||
/// <returns>Guid of stashed object</returns>
|
||||
public async Task<Guid> StashCredentialForCurrentAppAsync(DateTimeOffset expiry = default) =>
|
||||
await StashCredentialAsync(
|
||||
await _identityService.GetAccessTokenForApplicationAsync(),
|
||||
expiry);
|
||||
|
||||
/// <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 either
|
||||
/// execute the workflow locally or dispatch a message to an Agent
|
||||
/// to run the workflow.
|
||||
///
|
||||
/// The periodicUpdateFunction will be executed regularly throughout
|
||||
/// the execution of the workflow.
|
||||
///
|
||||
/// When dispatching a message, the dispatchMessageState string will
|
||||
/// be included to differentiate message sets
|
||||
/// </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="dispatchMessageState">State included in any dispatched messages to group related messages</param>
|
||||
/// <param name="providerConfigurations">Provider configurations</param>
|
||||
/// <returns></returns>
|
||||
public async Task DispatchOrExecuteAsync(
|
||||
TimeSpan secretValidPeriod,
|
||||
Func<ProviderWorkflowActionCollection, Task> periodicUpdateFunction,
|
||||
string dispatchMessageState,
|
||||
params ProviderExecutionParameters[] providerConfigurations)
|
||||
{
|
||||
if (providerConfigurations.Any(c => c.AgentId != _options.Value.InstanceId))
|
||||
{
|
||||
var agentId = providerConfigurations.FirstOrDefault(c => c.AgentId != _options.Value.InstanceId)?.AgentId;
|
||||
await _agentCommunicationProvider.Send(
|
||||
await AgentMessageEnvelope.Create(_cryptographicImplementation,
|
||||
_options.Value.InstanceId,
|
||||
agentId,
|
||||
new AgentProviderCommandMessage()
|
||||
{
|
||||
State = dispatchMessageState,
|
||||
ValidPeriod = secretValidPeriod,
|
||||
Providers = providerConfigurations.Where(c => c.AgentId == agentId).ToList()
|
||||
}));
|
||||
}
|
||||
|
||||
if (providerConfigurations.Any(c => c.AgentId == _options.Value.InstanceId))
|
||||
{
|
||||
await ExecuteAsync(secretValidPeriod,
|
||||
periodicUpdateFunction,
|
||||
providerConfigurations.Where(c => c.AgentId == _options.Value.InstanceId).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ using System;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace AuthJanitor.Integrations.CryptographicImplementations
|
||||
namespace AuthJanitor.CryptographicImplementations
|
||||
{
|
||||
public static class CryptographicImplementationExtensions
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Integrations.CryptographicImplementations
|
||||
namespace AuthJanitor.CryptographicImplementations
|
||||
{
|
||||
public interface ICryptographicImplementation : IAuthJanitorExtensibilityPoint
|
||||
{
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.Integrations.EventSinks;
|
||||
using AuthJanitor.EventSinks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.UI.Shared.MetaServices
|
||||
namespace AuthJanitor
|
||||
{
|
||||
public class EventDispatcherMetaService
|
||||
public class EventDispatcherService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IEnumerable<IEventSink> _eventSinks;
|
||||
|
||||
public EventDispatcherMetaService(
|
||||
public EventDispatcherService(
|
||||
ILoggerFactory loggerFactory,
|
||||
IEnumerable<IEventSink> eventSinks)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<EventDispatcherMetaService>();
|
||||
_logger = loggerFactory.CreateLogger<EventDispatcherService>();
|
||||
_eventSinks = eventSinks;
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ using AuthJanitor.EventSinks;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Integrations.EventSinks
|
||||
namespace AuthJanitor.EventSinks
|
||||
{
|
||||
public interface IEventSink : IAuthJanitorExtensibilityPoint
|
||||
{
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace AuthJanitor.Integrity
|
|||
public enum ExtensibilityTypes
|
||||
{
|
||||
CryptographicImplementation,
|
||||
DataStore,
|
||||
EventSink,
|
||||
Identity,
|
||||
Provider,
|
||||
|
@ -35,15 +34,11 @@ namespace AuthJanitor.Integrity
|
|||
{
|
||||
ExtensibilityType = ExtensibilityTypes.Identity;
|
||||
}
|
||||
else if (typeof(Integrations.CryptographicImplementations.ICryptographicImplementation).IsAssignableFrom(type))
|
||||
else if (typeof(CryptographicImplementations.ICryptographicImplementation).IsAssignableFrom(type))
|
||||
{
|
||||
ExtensibilityType = ExtensibilityTypes.CryptographicImplementation;
|
||||
}
|
||||
else if (typeof(Integrations.DataStores.IDataStore<>).IsAssignableFrom(type))
|
||||
{
|
||||
ExtensibilityType = ExtensibilityTypes.DataStore;
|
||||
}
|
||||
else if (typeof(Integrations.EventSinks.IEventSink).IsAssignableFrom(type))
|
||||
else if (typeof(EventSinks.IEventSink).IsAssignableFrom(type))
|
||||
{
|
||||
ExtensibilityType = ExtensibilityTypes.EventSink;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthenticodeExaminer;
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Integrity
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -10,14 +11,13 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Providers
|
||||
namespace AuthJanitor
|
||||
{
|
||||
public class ProviderManagerService
|
||||
{
|
||||
private const int MAX_RETRIES = 5;
|
||||
private const int DELAY_BETWEEN_ACTIONS_MS = 1000;
|
||||
public static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions()
|
||||
{
|
||||
WriteIndented = false,
|
||||
|
@ -50,13 +50,20 @@ namespace AuthJanitor.Providers
|
|||
.AsReadOnly();
|
||||
}
|
||||
|
||||
public static void ConfigureServices(IServiceCollection serviceCollection, params Type[] loadedProviderTypes)
|
||||
{
|
||||
serviceCollection.AddSingleton<ProviderManagerService>((s) => new ProviderManagerService(s, loadedProviderTypes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a provider is loaded
|
||||
/// </summary>
|
||||
/// <param name="providerName">Provider type name</param>
|
||||
/// <returns><c>TRUE</c> if the provider is loaded</returns>
|
||||
public bool HasProvider(string providerName) => LoadedProviders.Any(p => p.ProviderTypeName == providerName);
|
||||
|
||||
/// <summary>
|
||||
/// Get the metadata for a given Provider by its type name.
|
||||
///
|
||||
/// If the Provider is not found, an Exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="providerName">Provider type name</param>
|
||||
/// <returns>Provider's metadata</returns>
|
||||
public LoadedProviderMetadata GetProviderMetadata(string providerName)
|
||||
{
|
||||
if (!HasProvider(providerName))
|
||||
|
@ -65,12 +72,27 @@ namespace AuthJanitor.Providers
|
|||
return LoadedProviders.First(p => p.ProviderTypeName == providerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an unconfigured instance of a given Provider by its type name.
|
||||
///
|
||||
/// If the Provider is not found, an Exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="providerName">Provider type name</param>
|
||||
/// <returns>Unconfigured Provider instance</returns>
|
||||
public IAuthJanitorProvider GetProviderInstance(string providerName)
|
||||
{
|
||||
var metadata = GetProviderMetadata(providerName);
|
||||
return ActivatorUtilities.CreateInstance(_serviceProvider, metadata.ProviderType) as IAuthJanitorProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an instance of a given Provider by its type name.
|
||||
/// The Provider will be configured with its defaults.
|
||||
///
|
||||
/// If the Provider is not found, an Exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="providerName">Provider type name</param>
|
||||
/// <returns>Provider instance with defaults</returns>
|
||||
public IAuthJanitorProvider GetProviderInstanceDefault(string providerName)
|
||||
{
|
||||
var instance = GetProviderInstance(providerName);
|
||||
|
@ -78,6 +100,16 @@ namespace AuthJanitor.Providers
|
|||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an instance of a given Provider by its type name.
|
||||
/// The instance's configuration will be deserialized from
|
||||
/// the given string.
|
||||
///
|
||||
/// If the Provider is not found, an Exception is thrown.
|
||||
/// </summary>
|
||||
/// <param name="providerName">Provider type name</param>
|
||||
/// <param name="serializedProviderConfiguration">Serialized configuration</param>
|
||||
/// <returns>Configured Provider instance</returns>
|
||||
public IAuthJanitorProvider GetProviderInstance(string providerName, string serializedProviderConfiguration)
|
||||
{
|
||||
var instance = GetProviderInstance(providerName);
|
||||
|
@ -85,20 +117,62 @@ namespace AuthJanitor.Providers
|
|||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a duplicate of an existing Provider, including configuration.
|
||||
///
|
||||
/// If the Provider is not found, an Exception is thrown.
|
||||
/// </summary>
|
||||
/// <typeparam name="TProvider">Provider type</typeparam>
|
||||
/// <param name="existingProviderToClone">Provider to clone</param>
|
||||
/// <returns>Duplicate Provider instance</returns>
|
||||
public TProvider GetProviderInstance<TProvider>(TProvider existingProviderToClone)
|
||||
where TProvider : IAuthJanitorProvider =>
|
||||
(TProvider)GetProviderInstance(existingProviderToClone.GetType().AssemblyQualifiedName, existingProviderToClone.SerializedConfiguration);
|
||||
|
||||
/// <summary>
|
||||
/// Get the default configuration for a given Provider by its type name
|
||||
/// </summary>
|
||||
/// <param name="name">Provider type name</param>
|
||||
/// <returns>Default configuration for Provider</returns>
|
||||
public AuthJanitorProviderConfiguration GetProviderConfiguration(string name) => ActivatorUtilities.CreateInstance(_serviceProvider, GetProviderMetadata(name).ProviderConfigurationType) as AuthJanitorProviderConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Get the Provider configuration for a given Provider from
|
||||
/// a serialized string
|
||||
/// </summary>
|
||||
/// <param name="name">Provider type name</param>
|
||||
/// <param name="serializedConfiguration">Serialized configuration</param>
|
||||
/// <returns>Provider configuration</returns>
|
||||
public AuthJanitorProviderConfiguration GetProviderConfiguration(string name, string serializedConfiguration) => JsonSerializer.Deserialize(serializedConfiguration, GetProviderMetadata(name).ProviderConfigurationType, SerializerOptions) as AuthJanitorProviderConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Serialize a given Provider configuration
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Configuration type</typeparam>
|
||||
/// <param name="name">Provider type name</param>
|
||||
/// <param name="configuration">Configuration object</param>
|
||||
/// <returns>Serialized configuration</returns>
|
||||
public string GetProviderConfiguration<T>(string name, T configuration) => JsonSerializer.Serialize(configuration, GetProviderMetadata(name).ProviderConfigurationType, SerializerOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Test a given serialized configuration with a given
|
||||
/// Provider type name
|
||||
/// </summary>
|
||||
/// <param name="name">Provider type name</param>
|
||||
/// <param name="serializedConfiguration">Serialized configuration to test</param>
|
||||
/// <returns><c>TRUE</c> if the configuration is valid for this Provider</returns>
|
||||
public bool TestProviderConfiguration(string name, string serializedConfiguration)
|
||||
{
|
||||
try { return GetProviderConfiguration(name, serializedConfiguration) != null; }
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
/// <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(
|
||||
|
@ -120,6 +194,13 @@ namespace AuthJanitor.Providers
|
|||
return providers;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
@ -148,6 +229,9 @@ namespace AuthJanitor.Providers
|
|||
return new ProviderResourceSuggestion[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loaded Providers available to this runtime instance
|
||||
/// </summary>
|
||||
public IReadOnlyList<LoadedProviderMetadata> LoadedProviders { get; }
|
||||
|
||||
private TProvider DuplicateProvider<TProvider>(TProvider provider)
|
||||
|
@ -155,6 +239,14 @@ namespace AuthJanitor.Providers
|
|||
_serviceProvider.GetRequiredService<ProviderManagerService>()
|
||||
.GetProviderInstance(provider);
|
||||
|
||||
/// <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)
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
namespace AuthJanitor.Providers
|
||||
{
|
||||
public enum TokenSources
|
||||
{
|
||||
Unknown,
|
||||
Explicit,
|
||||
Persisted,
|
||||
ServicePrincipal,
|
||||
OBO
|
||||
}
|
||||
|
||||
public class ProviderExecutionParameters
|
||||
{
|
||||
public string ProviderType { get; set; }
|
||||
public string ProviderConfiguration { get; set; }
|
||||
|
||||
public TokenSources TokenSource { get; set; }
|
||||
public string TokenParameter { get; set; }
|
||||
public string AgentId { get; set; } = "admin-service";
|
||||
|
||||
public AccessTokenCredential AccessToken { get; set; }
|
||||
}
|
||||
}
|
|
@ -47,12 +47,9 @@ namespace AuthJanitor.Providers
|
|||
_serviceProvider.GetRequiredService<ProviderManagerService>()
|
||||
.GetProviderInstance(provider);
|
||||
|
||||
private IAuthJanitorProvider CreateProvider(string providerType, string providerConfiguration) =>
|
||||
_serviceProvider.GetRequiredService<ProviderManagerService>()
|
||||
.GetProviderInstance(providerType, providerConfiguration);
|
||||
|
||||
public void EmbedCredentials(AccessTokenCredential credential) =>
|
||||
_actions.ForEach(a => a.Instance.Credential = credential);
|
||||
_actions.Where(a => a.Instance.Credential == null).ToList()
|
||||
.ForEach(a => a.Instance.Credential = credential);
|
||||
|
||||
public void Add<TProvider>(params ProviderWorkflowActionWithoutResult<TProvider>[] actions)
|
||||
where TProvider : IAuthJanitorProvider
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.DataStores;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.UI.Shared.ViewModels;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -15,6 +11,8 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.DataStores;
|
||||
|
||||
namespace AuthJanitor.Services
|
||||
{
|
||||
|
@ -28,7 +26,7 @@ namespace AuthJanitor.Services
|
|||
private readonly IIdentityService _identityService;
|
||||
private readonly ICryptographicImplementation _cryptographicImplementation;
|
||||
private readonly ProviderManagerService _providerManager;
|
||||
private readonly EventDispatcherMetaService _eventDispatcher;
|
||||
private readonly EventDispatcherService _eventDispatcher;
|
||||
|
||||
private readonly IDataStore<ManagedSecret> _managedSecrets;
|
||||
private readonly IDataStore<Resource> _resources;
|
||||
|
@ -38,7 +36,7 @@ namespace AuthJanitor.Services
|
|||
IOptions<AuthJanitorCoreConfiguration> configuration,
|
||||
IIdentityService identityService,
|
||||
ICryptographicImplementation cryptographicImplementation,
|
||||
EventDispatcherMetaService eventDispatcher,
|
||||
EventDispatcherService eventDispatcher,
|
||||
ProviderManagerService providerManager,
|
||||
IDataStore<ManagedSecret> managedSecretStore,
|
||||
IDataStore<Resource> resourceStore,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.UI.Shared;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.UI.Shared.ViewModels;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
|
@ -26,7 +25,7 @@ namespace AuthJanitor.Services
|
|||
{
|
||||
private readonly IIdentityService _identityService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly EventDispatcherMetaService _eventDispatcher;
|
||||
private readonly EventDispatcherService _eventDispatcher;
|
||||
private readonly ProviderManagerService _providerManager;
|
||||
|
||||
private readonly Func<AuthJanitorProviderConfiguration, ProviderConfigurationViewModel> _configViewModel;
|
||||
|
@ -35,7 +34,7 @@ namespace AuthJanitor.Services
|
|||
public ProvidersService(
|
||||
IIdentityService identityService,
|
||||
ILogger<ProvidersService> logger,
|
||||
EventDispatcherMetaService eventDispatcher,
|
||||
EventDispatcherService eventDispatcher,
|
||||
ProviderManagerService providerManager,
|
||||
Func<AuthJanitorProviderConfiguration, ProviderConfigurationViewModel> configViewModelDelegate,
|
||||
Func<LoadedProviderMetadata, LoadedProviderViewModel> providerViewModelDelegate)
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.DataStores;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.UI.Shared;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.UI.Shared.ViewModels;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -29,7 +26,7 @@ namespace AuthJanitor.Services
|
|||
private readonly IIdentityService _identityService;
|
||||
private readonly TaskExecutionMetaService _taskExecutionMetaService;
|
||||
private readonly ProviderManagerService _providerManager;
|
||||
private readonly EventDispatcherMetaService _eventDispatcher;
|
||||
private readonly EventDispatcherService _eventDispatcher;
|
||||
|
||||
private readonly IDataStore<ManagedSecret> _managedSecrets;
|
||||
private readonly IDataStore<RekeyingTask> _rekeyingTasks;
|
||||
|
@ -39,7 +36,7 @@ namespace AuthJanitor.Services
|
|||
IOptions<AuthJanitorCoreConfiguration> configuration,
|
||||
IIdentityService identityService,
|
||||
TaskExecutionMetaService taskExecutionMetaService,
|
||||
EventDispatcherMetaService eventDispatcher,
|
||||
EventDispatcherService eventDispatcher,
|
||||
ProviderManagerService providerManager,
|
||||
IDataStore<ManagedSecret> managedSecretStore,
|
||||
IDataStore<RekeyingTask> rekeyingTaskStore,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.DataStores;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.UI.Shared.ViewModels;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -25,14 +23,14 @@ namespace AuthJanitor.Services
|
|||
{
|
||||
private readonly IIdentityService _identityService;
|
||||
private readonly ProviderManagerService _providerManager;
|
||||
private readonly EventDispatcherMetaService _eventDispatcher;
|
||||
private readonly EventDispatcherService _eventDispatcher;
|
||||
|
||||
private readonly IDataStore<Resource> _resources;
|
||||
private readonly Func<Resource, ResourceViewModel> _resourceViewModel;
|
||||
|
||||
public ResourcesService(
|
||||
IIdentityService identityService,
|
||||
EventDispatcherMetaService eventDispatcher,
|
||||
EventDispatcherService eventDispatcher,
|
||||
ProviderManagerService providerManager,
|
||||
IDataStore<Resource> resourceStore,
|
||||
Func<Resource, ResourceViewModel> resourceViewModelDelegate)
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.DataStores;
|
||||
using AuthJanitor.EventSinks;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.Providers;
|
||||
using AuthJanitor.UI.Shared;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -21,7 +18,7 @@ namespace AuthJanitor.Services
|
|||
{
|
||||
private readonly AuthJanitorCoreConfiguration _configuration;
|
||||
private readonly ProviderManagerService _providerManager;
|
||||
private readonly EventDispatcherMetaService _eventDispatcherMetaService;
|
||||
private readonly EventDispatcherService _eventDispatcherMetaService;
|
||||
|
||||
private readonly IDataStore<ManagedSecret> _managedSecrets;
|
||||
private readonly IDataStore<Resource> _resources;
|
||||
|
@ -29,7 +26,7 @@ namespace AuthJanitor.Services
|
|||
|
||||
public ScheduleRekeyingTasksService(
|
||||
IOptions<AuthJanitorCoreConfiguration> configuration,
|
||||
EventDispatcherMetaService eventDispatcherMetaService,
|
||||
EventDispatcherService eventDispatcherMetaService,
|
||||
ProviderManagerService providerManager,
|
||||
IDataStore<ManagedSecret> managedSecretStore,
|
||||
IDataStore<Resource> resourceStore,
|
||||
|
|
|
@ -89,9 +89,6 @@ namespace AuthJanitor
|
|||
builder.Services.AddTransient<ProvidersService>();
|
||||
builder.Services.AddTransient<ResourcesService>();
|
||||
|
||||
logger.LogDebug("Registering AuthJanitor MetaServices");
|
||||
AuthJanitorServiceRegistration.RegisterServices(builder.Services);
|
||||
|
||||
// -----
|
||||
|
||||
logger.LogDebug("Registering DataStores");
|
||||
|
@ -119,7 +116,7 @@ namespace AuthJanitor
|
|||
|
||||
logger.LogInformation("Found {ProviderCount} providers: {ProviderTypeNames}", providerTypes.Length, string.Join(" ", providerTypes.Select(t => t.Name)));
|
||||
logger.LogInformation("Registering Provider Manager Service");
|
||||
ProviderManagerService.ConfigureServices(builder.Services, providerTypes);
|
||||
builder.Services.AddAuthJanitorService("admin-service", providerTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using AuthJanitor.UI.Shared;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.DataStores;
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.UI.Shared;
|
||||
using AuthJanitor.UI.Shared.MetaServices;
|
||||
using AuthJanitor.UI.Shared.Models;
|
||||
using AuthJanitor.Integrations.DataStores;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using AuthJanitor.DataStores;
|
||||
|
||||
namespace AuthJanitor
|
||||
{
|
||||
|
|
|
@ -69,9 +69,6 @@ namespace AuthJanitor
|
|||
o.VaultName = "vault";
|
||||
});
|
||||
|
||||
logger.LogDebug("Registering AuthJanitor MetaServices");
|
||||
AuthJanitorServiceRegistration.RegisterServices(builder.Services);
|
||||
|
||||
// -----
|
||||
|
||||
logger.LogDebug("Registering DataStores");
|
||||
|
@ -98,8 +95,8 @@ namespace AuthJanitor
|
|||
.ToArray();
|
||||
|
||||
logger.LogInformation("Found {ProviderCount} providers: {ProviderTypeNames}", providerTypes.Length, string.Join(" ", providerTypes.Select(t => t.Name)));
|
||||
logger.LogInformation("Registering Provider Manager Service");
|
||||
ProviderManagerService.ConfigureServices(builder.Services, providerTypes);
|
||||
logger.LogInformation("Registering AuthJanitor Service");
|
||||
builder.Services.AddAuthJanitorService("agent-abc123", providerTypes); // todo: change this to env var
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using Azure.Security.KeyVault.Keys;
|
||||
using Azure.Security.KeyVault.Keys.Cryptography;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
|
||||
namespace AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.EventSinks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.IdentityServices;
|
||||
using AuthJanitor.SecureStorage;
|
||||
using Azure.Security.KeyVault.Secrets;
|
||||
|
@ -9,7 +8,7 @@ using System;
|
|||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using System.Buffers.Text;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
|
||||
namespace AuthJanitor.Integrations.SecureStorage.AzureKeyVault
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Microsoft.Azure.Management.AppService.Fluent;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Azure.Core;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Microsoft.Azure.Management.Maps;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
|||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Azure.Management.Search.Fluent;
|
||||
using Microsoft.Azure.Management.Search.Fluent.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Microsoft.Azure.Management.Fluent;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Azure.Management.Search.Fluent;
|
||||
using Microsoft.Azure.Management.Sql.Fluent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AuthJanitor.Providers.AzureSql
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
@ -8,7 +8,6 @@ using Microsoft.Azure.Management.CosmosDB.Fluent;
|
|||
using Microsoft.Azure.Management.Fluent;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
@ -9,8 +9,6 @@ using Microsoft.Azure.Management.EventHub.Fluent.Models;
|
|||
using Microsoft.Azure.Management.Fluent;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Azure.Management.Sql.Fluent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Azure;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Azure;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
using Azure;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.Azure.Management.Redis.Fluent;
|
|||
using Microsoft.Azure.Management.Redis.Fluent.Models;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
||||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
|||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Azure.Management.ServiceBus.Fluent;
|
||||
using Microsoft.Azure.Management.ServiceBus.Fluent.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Providers.Azure;
|
||||
using AuthJanitor.Providers.Azure.Workflows;
|
||||
using AuthJanitor.Providers.Capabilities;
|
||||
|
@ -9,7 +9,6 @@ using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
|
|||
using Microsoft.Azure.Management.ResourceManager.Fluent.Core.CollectionActions;
|
||||
using Microsoft.Azure.Management.Storage.Fluent;
|
||||
using Microsoft.Azure.Management.Storage.Fluent.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
using AuthJanitor.CryptographicImplementations;
|
||||
using AuthJanitor.Integrations.CryptographicImplementations;
|
||||
using AuthJanitor.Integrations.CryptographicImplementations.Default;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
|
@ -67,7 +67,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Tests", "AuthJa
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.DataStores.EntityFrameworkCore", "AuthJanitor.Integrations.DataStores.EntityFrameworkCore\AuthJanitor.Integrations.DataStores.EntityFrameworkCore.csproj", "{BCEC6AD5-4A69-4440-8643-AED247F45281}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault", "AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault\AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault.csproj", "{F5F9C392-D02D-4501-BE85-8DDAAC5D06F3}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault", "AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault\AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault.csproj", "{F5F9C392-D02D-4501-BE85-8DDAAC5D06F3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{C8572CCA-36EC-4AAB-A4C0-AF23B4B82B8C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -186,6 +188,7 @@ Global
|
|||
{12CA268F-9E1F-403F-8428-C36F1F1C17E1} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0}
|
||||
{1EB29D3F-A7E4-4038-9FA9-27CAD63213FA} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0}
|
||||
{03A38E05-A6D4-4325-A3A4-6AE33D7E6718} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0}
|
||||
{9C0E454F-146E-40FB-AFE5-F63DAACAB326} = {C8572CCA-36EC-4AAB-A4C0-AF23B4B82B8C}
|
||||
{8DBA0BBC-F92B-456D-8342-0C42E39EB81E} = {4AB5E96D-1A78-44E0-A52B-5F1A7ABE3B40}
|
||||
{F9DCAB83-056E-4F4B-A8CA-9DDBBCF08394} = {0E2B33F8-6CD0-43EE-AA35-337B6798B509}
|
||||
{5BDB5B18-884B-4BB5-BF20-A06114C5C928} = {45A0E86E-0F1A-434E-B3F9-D9AE88C964E1}
|
||||
|
@ -193,10 +196,12 @@ Global
|
|||
{4AB5E96D-1A78-44E0-A52B-5F1A7ABE3B40} = {45A0E86E-0F1A-434E-B3F9-D9AE88C964E1}
|
||||
{CC3E1903-9A04-4A14-8EDB-59444BB57E05} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0}
|
||||
{0E2B33F8-6CD0-43EE-AA35-337B6798B509} = {45A0E86E-0F1A-434E-B3F9-D9AE88C964E1}
|
||||
{07EE807A-D975-4B1D-B486-6633ED87B757} = {C8572CCA-36EC-4AAB-A4C0-AF23B4B82B8C}
|
||||
{31D82FCB-3F22-482C-B7A4-F9D9D4A27EF6} = {07EE807A-D975-4B1D-B486-6633ED87B757}
|
||||
{1E8E5F06-CF36-4CC8-95BD-B7E455D1737C} = {07EE807A-D975-4B1D-B486-6633ED87B757}
|
||||
{367C9AAB-6A24-4DC8-A69C-A22039CC11C2} = {07EE807A-D975-4B1D-B486-6633ED87B757}
|
||||
{03C30803-4304-4C01-B84B-3FFFA2AD57A1} = {07EE807A-D975-4B1D-B486-6633ED87B757}
|
||||
{F103367D-F5FB-43A6-AA52-6B9A0DD66862} = {C8572CCA-36EC-4AAB-A4C0-AF23B4B82B8C}
|
||||
{2ED2CBEF-1B5F-4D0D-ACF0-C41D59C69B7A} = {07EE807A-D975-4B1D-B486-6633ED87B757}
|
||||
{A22F4B5A-10D8-4692-9C34-D7089108E4B5} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0}
|
||||
{AF99E01C-3AB2-4AF6-8771-871B3F0E380E} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0}
|
||||
|
|
Загрузка…
Ссылка в новой задаче