diff --git a/src/AuthJanitor.AspNet.AdminUi/App.razor b/src/AuthJanitor.AspNet.AdminUi/App.razor deleted file mode 100644 index 4cf8f0d..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/App.razor +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - -
We looked high and we looked low, but there's nothing here by that name.
-
-
-
- -@using Blazorise -@code{ - private Theme theme = new Theme - { - IsGradient = true, - IsRounded = true - }; -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/AuthJanitor.AspNet.AdminUi.csproj b/src/AuthJanitor.AspNet.AdminUi/AuthJanitor.AspNet.AdminUi.csproj deleted file mode 100644 index 6c97de8..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/AuthJanitor.AspNet.AdminUi.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - netstandard2.1 - 3.0 - Debug;Release - AuthJanitor.UI - - - - false - - - - - - - - - - - - - - - - - - - - diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalSummary.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalSummary.razor deleted file mode 100644 index 466e64d..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalSummary.razor +++ /dev/null @@ -1,60 +0,0 @@ - - - @if (TotalRisks > 0) - { - - There are @TotalRisks identified risks, with a score of @TotalRiskScore. - - } - else - { - - There are no identified risks for this Rekeying Task! - - } - - @if (TotalRisks > 0) - { -
-
-
-
-
Risk
-
Remediation
-
- @foreach (var risk in AllRisks) - { -
-
-
- - - @risk.Score - -
- -
-
-
@risk.Risk
-
-
-

@risk.Recommendation

-
-
- } -
-
- } -
- -@code { - [Parameter] - public RekeyingTaskViewModel Task { get; set; } = new RekeyingTaskViewModel(); - - public int TotalRisks => Task.ManagedSecret == null ? 0 : - Task.ManagedSecret.Resources.Sum(r => r.Risks.Count()); - public int TotalRiskScore => Task.ManagedSecret == null ? 0 : - Task.ManagedSecret.Resources.Sum(r => r.RiskScore); - public List AllRisks => Task.ManagedSecret == null ? new List() : - Task.ManagedSecret.Resources.SelectMany(r => r.Risks).ToList(); -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalTypeButtonComponent.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalTypeButtonComponent.razor deleted file mode 100644 index 8dd2c14..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalTypeButtonComponent.razor +++ /dev/null @@ -1,41 +0,0 @@ - - -@code { - [Parameter] - public bool IsDisabled { get; set; } - - [Parameter] - public EventCallback IsDisabledChanged { get; set; } - - [Parameter] - public bool IsSelected { get; set; } - - [Parameter] - public EventCallback IsSelectedChanged { get; set; } - - [Parameter] - public string Icon { get; set; } - - [Parameter] - public string Description { get; set; } - - [Parameter] - public string SelectedCssClasses { get; set; } = "bg-warning"; - - [Parameter] - public string NotSelectedCssClasses { get; set; } = ""; - - - private string CssClasses => (IsSelected ? SelectedCssClasses : NotSelectedCssClasses) + " iconLit"; - - protected void ChangeValue() - { - IsSelected = !IsSelected; - IsSelectedChanged.InvokeAsync(IsSelected); - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalTypeComponent.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalTypeComponent.razor deleted file mode 100644 index 246e3c4..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ApprovalTypeComponent.razor +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - -@code { - [Parameter] public TaskConfirmationStrategies Value { get; set; } - [Parameter] public EventCallback ValueChanged { get; set; } - - [Parameter] - public bool ShowEditControls { get; set; } = false; - - protected void UpdateValue(TaskConfirmationStrategies flag, bool isSelected) - { - if (Value.HasFlag(flag) && !isSelected) - Value &= ~flag; - else if (!Value.HasFlag(flag) && isSelected) - Value |= flag; - ValueChanged.InvokeAsync(Value); - StateHasChanged(); - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/BreadcrumbRow.razor b/src/AuthJanitor.AspNet.AdminUi/Components/BreadcrumbRow.razor deleted file mode 100644 index bd23280..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/BreadcrumbRow.razor +++ /dev/null @@ -1,52 +0,0 @@ -
-
- -
- @if (ChildContent != null) - { - @ChildContent - } -
- -@code -{ - [Parameter] - public RenderFragment ChildContent { get; set; } - - [Parameter] - public string Category { get; set; } - - [Parameter] - public string PageGroup { get; set; } - - [Parameter] - public string PageGroupLink { get; set; } - - [Parameter] - public string OptionalObjectName { get; set; } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ColorTitledGroupBox.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ColorTitledGroupBox.razor deleted file mode 100644 index 3451f2d..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ColorTitledGroupBox.razor +++ /dev/null @@ -1,26 +0,0 @@ - - @Title - @ChildContent - - -@code -{ - [Parameter] - public string CssClass { get; set; } - - [Parameter] - public string Title { get; set; } = "New Group Box"; - - [Parameter] - public Background BackgroundColor { get; set; } = Background.Primary; - - [Parameter] - public TextColor TextColor { get; set; } = TextColor.Dark; - - [Parameter] - public RenderFragment ChildContent { get; set; } - - protected string BackgroundColorClass => $"bg-{BackgroundColor.ToString().ToLower()}"; - protected string BorderColorClass => $"border-{BackgroundColor.ToString().ToLower()}"; - protected string TextColorClass => $"text-{TextColor.ToString().ToLower()}"; -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ColoredProgressBar.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ColoredProgressBar.razor deleted file mode 100644 index f10178f..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ColoredProgressBar.razor +++ /dev/null @@ -1,19 +0,0 @@ -
-
@($"{Value}%")
-
- -@code -{ - public string BackgroundColor - { - get - { - if (Value > 90) return "bg-danger"; - else if (Value > 75) return "bg-warning"; - else return "bg-success"; - } - } - - [Parameter] - public int Value { get; set; } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ColoredRiskScore.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ColoredRiskScore.razor deleted file mode 100644 index a641027..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ColoredRiskScore.razor +++ /dev/null @@ -1,27 +0,0 @@ -
- - @if (ShowRiskText) - { - Risk Score · - } - @Value - -
- -@code { - public string BadgeColor - { - get - { - if (Value > 90) return "badge-danger"; - else if (Value > 75) return "badge-warning"; - else return "badge-success"; - } - } - - [Parameter] - public int Value { get; set; } - - [Parameter] - public bool ShowRiskText { get; set; } = false; -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationItemEditor.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationItemEditor.razor deleted file mode 100644 index f8e0348..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationItemEditor.razor +++ /dev/null @@ -1,29 +0,0 @@ -@switch (Value.InputType) -{ - case ProviderConfigurationItemViewModel.InputTypes.TextArray: - case ProviderConfigurationItemViewModel.InputTypes.Text: - - break; - case ProviderConfigurationItemViewModel.InputTypes.Integer: - - break; - case ProviderConfigurationItemViewModel.InputTypes.Boolean: - - break; - case ProviderConfigurationItemViewModel.InputTypes.Enumeration: - - break; -} - -@code { - [Parameter] - public ProviderConfigurationItemViewModel Value { get; set; } - - [Parameter] - public EventCallback ValueChanged { get; set; } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationTestButton.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationTestButton.razor deleted file mode 100644 index e7708bc..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationTestButton.razor +++ /dev/null @@ -1,81 +0,0 @@ - - -@using System.Text.Json -@code { - [Parameter] - public TestAsContexts Context { get; set; } - - [Parameter] - public string ProviderType { get; set; } - - [Parameter] - public ProviderConfigurationViewModel ProviderConfiguration { get; set; } - - [Parameter] - public string DefaultColor { get; set; } = "btn-secondary"; - - protected string ButtonColor { get; set; } - protected string IsCollapsed { get; set; } = "collapse"; - protected string LastMessage { get; set; } = string.Empty; - - protected string Icon => Context switch - { - TestAsContexts.AsApp => "fa fa-robot", - TestAsContexts.AsUser => "fa fa-user", - _ => "fa fa-question" - }; - - protected string Text => Context switch - { - TestAsContexts.AsApp => "Test as App", - TestAsContexts.AsUser => "Test as User", - _ => "?? Context ??" - }; - - protected async Task ButtonClicked() - { - IsCollapsed = string.Empty; - ButtonColor = "btn-warning"; - - StateHasChanged(); - - try - { - var result = await Http.PostAsync($"{Http.BaseAddress}/providers/{ProviderType}/test/{Context.ToString()}", - new System.Net.Http.StringContent(ProviderConfiguration.SerializedConfiguration)); - if (result.IsSuccessStatusCode) - { - LastMessage = "Success!"; - ButtonColor = "btn-success"; - } - else - { - var messageStructure = JsonSerializer.Deserialize>( - await result.Content.ReadAsStringAsync()); - LastMessage = messageStructure["Message"]; - ButtonColor = "btn-danger"; - } - } - catch (Exception ex) - { - LastMessage = ex.Message; - ButtonColor = "btn-danger"; - } - - IsCollapsed = "collapse"; - StateHasChanged(); - } - - protected override void OnInitialized() - { - ButtonColor = DefaultColor; - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationVisualizer.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationVisualizer.razor deleted file mode 100644 index a10daea..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ConfigurationVisualizer.razor +++ /dev/null @@ -1,52 +0,0 @@ - - - @if (Value != null && Value.ConfigurationItems != null && Value.ConfigurationItems.Any()) - { - @for (var i = 0; i < Value.ConfigurationItems.ToArray().Length; i++) - { - var setting = Value.ConfigurationItems.ToArray()[i]; - - - @if (!string.IsNullOrEmpty(setting.HelpText)) - { - - - - } - else - { -   - } - - @setting.DisplayName - - - - @if (ShowEditControls) - { - - } - else - { - @setting.Value - } - - - } - } - -
- -@code { - [Parameter] - public ProviderConfigurationViewModel Value { get; set; } - - [Parameter] - public EventCallback ValueChanged { get; set; } - - [Parameter] - public bool ShowEditControls { get; set; } - - [Parameter] - public EventCallback ShowEditControlsChanged { get; set; } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/DashboardInformationCard.razor b/src/AuthJanitor.AspNet.AdminUi/Components/DashboardInformationCard.razor deleted file mode 100644 index 5f45ed0..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/DashboardInformationCard.razor +++ /dev/null @@ -1,24 +0,0 @@ -
-
-
-
@Heading
-

@Data

-
-
- -@code { - [Parameter] - public string TextColor { get; set; } = "text-white"; - - [Parameter] - public string BgColor { get; set; } = "bg-primary"; - - [Parameter] - public string Heading { get; set; } - - [Parameter] - public string Data { get; set; } - - [Parameter] - public string Icon { get; set; } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/HelpSlideInComponent.razor b/src/AuthJanitor.AspNet.AdminUi/Components/HelpSlideInComponent.razor deleted file mode 100644 index 41e9195..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/HelpSlideInComponent.razor +++ /dev/null @@ -1,24 +0,0 @@ -
-

- - @Title -

- @ChildContent -
- -@code { - [Parameter] - public string Title { get; set; } - - [Parameter] - public string Icon { get; set; } - - [Parameter] - public bool Visible { get; set; } - - [Parameter] - public EventCallback VisibleChanged { get; set; } - - [Parameter] - public RenderFragment ChildContent { get; set; } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ProviderCapabilitiesComponent.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ProviderCapabilitiesComponent.razor deleted file mode 100644 index a474376..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ProviderCapabilitiesComponent.razor +++ /dev/null @@ -1,48 +0,0 @@ -@{ - void RenderCapabilityIcon(string icon, string templateString, bool isLit) - { - - } - - RenderCapabilityIcon( - icon: FontAwesomeIcons.Flask, - templateString: "Provider {0} running sanity tests", - isLit: HasCapabilities(ProviderCapabilities.CanRunSanityTests)); - - RenderCapabilityIcon( - icon: FontAwesomeIcons.SyncAlt, - templateString: "Provider {0} generating/consuming interim secret values", - isLit: HasCapabilitiesOr(ProviderCapabilities.CanDistributeTemporarySecrets, - ProviderCapabilities.CanGenerateTemporarySecrets)); - - RenderCapabilityIcon( - icon: FontAwesomeIcons.LayerGroup, - templateString: "Provider {0} resource candidate selection for configuration", - isLit: HasCapabilities(ProviderCapabilities.CanEnumerateResourceCandidates)); -} - -@code { - [Parameter] - public LoadedProviderViewModel Provider { get; set; } - - [Parameter] - public string IconLitColor { get; set; } = "text-success"; - - [Parameter] - public string IconUnlitColor { get; set; } = "text-dark"; - - private const string SUPPORTS = "supports"; - private const string DOES_NOT_SUPPORT = "does not support"; - - private static string GetTitle(string templateString, bool isLit) => - string.Format(templateString, isLit ? SUPPORTS : DOES_NOT_SUPPORT); - - private bool HasCapabilities(params ProviderCapabilities[] capabilities) => - !capabilities.Except(Provider.Capabilities).Any(); - - private bool HasCapabilitiesOr(params ProviderCapabilities[] capabilities) => - Provider.Capabilities.Any(c => capabilities.Contains(c)); -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/ProviderSelector.razor b/src/AuthJanitor.AspNet.AdminUi/Components/ProviderSelector.razor deleted file mode 100644 index bfbec85..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/ProviderSelector.razor +++ /dev/null @@ -1,35 +0,0 @@ - - -@using AuthJanitor.UI.Shared.ViewModels -@code { - [Parameter] - public string Value { get; set; } - - [Parameter] - public EventCallback ValueChanged { get; set; } - - protected IEnumerable Providers { get; set; } = new List(); - - protected override async Task OnInitializedAsync() => Providers = await Http.AJList(); - - protected Task SelectedValueChanged(string value) - { - return ValueChanged.InvokeAsync(value); - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/RekeyingTaskAttemptAccordion.razor b/src/AuthJanitor.AspNet.AdminUi/Components/RekeyingTaskAttemptAccordion.razor deleted file mode 100644 index 316136b..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/RekeyingTaskAttemptAccordion.razor +++ /dev/null @@ -1,74 +0,0 @@ - - @foreach (var action in Action.Actions) - { - var bgColor = ""; - if (action.HasCompleted && action.HasSucceeded) - bgColor = "border-left-success"; - else if (action.HasCompleted && !action.HasSucceeded) - bgColor = "border-left-danger"; - else if (action.HasStarted) - bgColor = "border-left-info"; - else bgColor = "border-left-dark"; - - - - - @action.ExecutionOrder - @{ - var isDimmed = "text-gray-500"; - } - - - @if (action.End.Year > 10) - { - @(Math.Round((action.End - action.Start).TotalSeconds, 2) + "sec") - } - else if (action.Start.Year > 10) - { - @(Math.Round((DateTimeOffset.UtcNow - action.Start).TotalSeconds, 2) + "sec") - } - - - - -

@action.Start.ToString("d") · @action.Start.ToString("T") - @action.End.ToString("T")

-
@action.Log
- @if (!string.IsNullOrEmpty(action.Exception)) - { - - } -
-
- } -
- -@code { - Dictionary _actionVisibilities = new Dictionary(); - public TimeSpan DurationSoFar => DateTimeOffset.UtcNow - Secret.LastChanged.GetValueOrDefault(); - protected IEnumerable _providers; - - [Parameter] - public ManagedSecretViewModel Secret { get; set; } - - [Parameter] - public ProviderWorkflowActionCollection Action { get; set; } - - protected override async Task OnInitializedAsync() => - _providers = await Http.AJList(); -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/SystemWideFooter.razor b/src/AuthJanitor.AspNet.AdminUi/Components/SystemWideFooter.razor deleted file mode 100644 index 459fc2e..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/SystemWideFooter.razor +++ /dev/null @@ -1,69 +0,0 @@ -
- - -@code -{ - protected DashboardMetricsViewModel Metrics { get; set; } = new DashboardMetricsViewModel(); - - [Parameter] - public EventCallback RefreshDataClicked { get; set; } - - [Parameter] - public bool ContextualHelpVisible { get; set; } - - [Parameter] - public EventCallback ContextualHelpVisibleChanged { get; set; } - - protected void ChangeContextualHelpVisibility() - { - ContextualHelpVisible = !ContextualHelpVisible; - if (!ContextualHelpVisibleChanged.Equals(default(EventCallback))) - ContextualHelpVisibleChanged.InvokeAsync(ContextualHelpVisible); - } - - protected override async Task OnInitializedAsync() - { - Metrics = await Http.AJGet(); - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/TaskProgressionComponent.razor b/src/AuthJanitor.AspNet.AdminUi/Components/TaskProgressionComponent.razor deleted file mode 100644 index 5a7a86c..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/TaskProgressionComponent.razor +++ /dev/null @@ -1,89 +0,0 @@ - - - - -
- - - - - - - - - Temporary Secrets -
- - -
- - - - - - - - - Long-Term Secrets -
- - - - - - - -@code { - [Parameter] - public ProviderWorkflowActionCollection Attempt { get; set; } - - // TODO: Everything here is a hack to parse the log for indicators - // Need to move provider actions into their own objects to track this better - private const string TEST_PHASE_INDICATOR = "### Performing provider tests..."; - - private const string GENERATE_TEMPORARY_PHASE_INDICATOR = "### Retrieving/generating temporary secrets..."; - private const string DISTRIBUTE_TEMPORARY_PHASE_INDICATOR = "### Distributing temporary secrets..."; - private const string COMMIT_TEMPORARY_PHASE_INDICATOR = "### Performing commits for temporary secrets..."; - - private const string GENERATE_LONG_TERM_PHASE_INDICATOR = "### Rekeying objects and services..."; - private const string DISTRIBUTE_LONG_TERM_PHASE_INDICATOR = "### Distributing regenerated secrets..."; - private const string COMMIT_LONG_TERM_PHASE_INDICATOR = "### Performing commits..."; - private const string CLEANUP_PHASE_INDICATOR = "### Running cleanup operations..."; - private const string END_PROCESS_INDICATOR = "########## END REKEYING WORKFLOW ##########"; - - private const string PHASE_NOT_STARTED_COLOR = "fa-3x text-secondary"; - private const string PHASE_IN_PROGRESS_COLOR = "fa-3x text-warning"; - private const string PHASE_COMPLETE_COLOR = "fa-3x text-success"; - private const string PHASE_ERROR_COLOR = "fa-3x text-danger"; - - public string TestPhaseColor => GetColorFromLogLines(TEST_PHASE_INDICATOR, GENERATE_TEMPORARY_PHASE_INDICATOR); - - public string GenerateTemporaryPhaseColor => GetColorFromLogLines(GENERATE_TEMPORARY_PHASE_INDICATOR, DISTRIBUTE_TEMPORARY_PHASE_INDICATOR); - public string DistributeTemporaryPhaseColor => GetColorFromLogLines(DISTRIBUTE_TEMPORARY_PHASE_INDICATOR, COMMIT_TEMPORARY_PHASE_INDICATOR); - public string CommitTemporaryPhaseColor => GetColorFromLogLines(COMMIT_TEMPORARY_PHASE_INDICATOR, GENERATE_LONG_TERM_PHASE_INDICATOR); - - public string GenerateLongTermPhaseColor => GetColorFromLogLines(GENERATE_LONG_TERM_PHASE_INDICATOR, DISTRIBUTE_LONG_TERM_PHASE_INDICATOR); - public string DistributeLongTermPhaseColor => GetColorFromLogLines(DISTRIBUTE_LONG_TERM_PHASE_INDICATOR, COMMIT_LONG_TERM_PHASE_INDICATOR); - public string CommitLongTermPhaseColor => GetColorFromLogLines(COMMIT_LONG_TERM_PHASE_INDICATOR, CLEANUP_PHASE_INDICATOR); - - public string CleanupPhaseColor => GetColorFromLogLines(CLEANUP_PHASE_INDICATOR, END_PROCESS_INDICATOR); - public string CompletePhaseColor => GetColorFromLogLines(END_PROCESS_INDICATOR); - - protected string GetColorFromLogLines(string leftBookend, string rightBookend = null) - { - if (Attempt == null) return PHASE_NOT_STARTED_COLOR; - if (Attempt.OrchestrationLog == null) return PHASE_NOT_STARTED_COLOR; - - if (Attempt.OrchestrationLog.Contains(leftBookend)) - { - if (string.IsNullOrEmpty(rightBookend)) - return PHASE_COMPLETE_COLOR; - if (Attempt.OrchestrationLog.Contains(rightBookend)) - return PHASE_COMPLETE_COLOR; - if (!string.IsNullOrEmpty(Attempt.GetLastException())) - return PHASE_ERROR_COLOR; - return PHASE_IN_PROGRESS_COLOR; - } - return PHASE_NOT_STARTED_COLOR; - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Components/TimespanComponent.razor b/src/AuthJanitor.AspNet.AdminUi/Components/TimespanComponent.razor deleted file mode 100644 index bf2e34e..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Components/TimespanComponent.razor +++ /dev/null @@ -1,33 +0,0 @@ -@if (ShowEditControls) -{ - -
- @if (TimeSpan.FromMinutes(Value).TotalHours >= 1) - { - @TimeSpan.FromMinutes(Value).ToReadableString() - } -} -else -{ - @TimeSpan.FromMinutes(Value).ToReadableString() -
- @if (TimeSpan.FromMinutes(Value).TotalHours >= 1) - { - (@Value minutes) - } -} - -@code -{ - [Parameter] - public bool ShowEditControls { get; set; } - - [Parameter] - public EventCallback ShowEditControlsChanged { get; set; } - - [Parameter] - public int Value { get; set; } - - [Parameter] - public EventCallback ValueChanged { get; set; } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Editors/AuthorizedUserEditor.razor b/src/AuthJanitor.AspNet.AdminUi/Editors/AuthorizedUserEditor.razor deleted file mode 100644 index d13a826..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Editors/AuthorizedUserEditor.razor +++ /dev/null @@ -1,29 +0,0 @@ - - - - User Principal Name - - - - - - Role - - - - - - - -@code { - [Parameter] - public AuthJanitorAuthorizedUserViewModel Value { get; set; } - - [Parameter] - public EventCallback ValueChanged { get; set; } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Editors/ManagedSecretEditor.razor b/src/AuthJanitor.AspNet.AdminUi/Editors/ManagedSecretEditor.razor deleted file mode 100644 index 2f89c42..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Editors/ManagedSecretEditor.razor +++ /dev/null @@ -1,70 +0,0 @@ - - @foreach (var item in Resources.OrderBy(r => r.Name)) - { - - - @((MarkupString)item.Provider.Details.SvgImage) - - - @item.Name - @item.Description - - - } - - - - - - Name - - - - - - Description - - - - - - Valid Period - - - - - - - - Approval Type(s) - - - - - - - -@code { - [Parameter] - public ManagedSecretViewModel Value { get; set; } - - [Parameter] - public EventCallback ValueChanged { get; set; } - - protected IEnumerable Resources { get; set; } = new List(); - - protected override async Task OnInitializedAsync() - { - Resources = await Http.AJList(); - } - - protected void UpdateObject(Guid objectId, bool isChecked) - { - var resourceIds = Value.ResourceIds.Split(';').ToList(); - if (isChecked) - resourceIds.Add(objectId.ToString()); - else - resourceIds.RemoveAll(r => Guid.Parse(r) == objectId); - Value.ResourceIds = string.Join(';', resourceIds.Where(r => !string.IsNullOrEmpty(r))); - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Editors/ResourceEditor.razor b/src/AuthJanitor.AspNet.AdminUi/Editors/ResourceEditor.razor deleted file mode 100644 index 45efc74..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Editors/ResourceEditor.razor +++ /dev/null @@ -1,72 +0,0 @@ - - - - Name - - - - - - - - Description - - - - - - - - Provider - - - - - - -@if (Value.ProviderConfiguration != null && - Value.ProviderConfiguration.ConfigurationItems != null && - Value.ProviderConfiguration.ConfigurationItems.Any()) -{ - - Provider Settings - - - - - - - Use the buttons below to test the configuration in the context of either the current user or the AuthJanitor application. - - - - - - - - -} - -@code { - [Parameter] - public ResourceViewModel Value { get; set; } = new ResourceViewModel(); - - [Parameter] - public EventCallback ValueChanged { get; set; } - - protected async Task ProviderTypeChanged(string providerType) - { - Value.ProviderType = providerType; - Value.ProviderConfiguration = await Http.AJGet(providerType); - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/FontAwesomeIcons.cs b/src/AuthJanitor.AspNet.AdminUi/FontAwesomeIcons.cs deleted file mode 100644 index 83b9ea1..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/FontAwesomeIcons.cs +++ /dev/null @@ -1,1553 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -public static class FontAwesomeIcons -{ - //Solid Icons - public static readonly string Ad = "fas fa-ad fa-fw"; - public static readonly string AddressBook = "fas fa-address-book fa-fw"; - public static readonly string AddressCard = "fas fa-address-card fa-fw"; - public static readonly string Adjust = "fas fa-adjust fa-fw"; - public static readonly string AirFreshener = "fas fa-air-freshener fa-fw"; - public static readonly string AlignCenter = "fas fa-align-center fa-fw"; - public static readonly string AlignJustify = "fas fa-align-justify fa-fw"; - public static readonly string AlignLeft = "fas fa-align-left fa-fw"; - public static readonly string AlignRight = "fas fa-align-right fa-fw"; - public static readonly string Allergies = "fas fa-allergies fa-fw"; - public static readonly string Ambulance = "fas fa-ambulance fa-fw"; - public static readonly string AmericanSignLanguageInterpreting = "fas fa-american-sign-language-interpreting fa-fw"; - public static readonly string Anchor = "fas fa-anchor fa-fw"; - public static readonly string AngleDoubleDown = "fas fa-angle-double-down fa-fw"; - public static readonly string AngleDoubleLeft = "fas fa-angle-double-left fa-fw"; - public static readonly string AngleDoubleRight = "fas fa-angle-double-right fa-fw"; - public static readonly string AngleDoubleUp = "fas fa-angle-double-up fa-fw"; - public static readonly string AngleDown = "fas fa-angle-down fa-fw"; - public static readonly string AngleLeft = "fas fa-angle-left fa-fw"; - public static readonly string AngleRight = "fas fa-angle-right fa-fw"; - public static readonly string AngleUp = "fas fa-angle-up fa-fw"; - public static readonly string Angry = "fas fa-angry fa-fw"; - public static readonly string Ankh = "fas fa-ankh fa-fw"; - public static readonly string AppleAlt = "fas fa-apple-alt fa-fw"; - public static readonly string Archive = "fas fa-archive fa-fw"; - public static readonly string Archway = "fas fa-archway fa-fw"; - public static readonly string ArrowAltCircleDown = "fas fa-arrow-alt-circle-down fa-fw"; - public static readonly string ArrowAltCircleLeft = "fas fa-arrow-alt-circle-left fa-fw"; - public static readonly string ArrowAltCircleRight = "fas fa-arrow-alt-circle-right fa-fw"; - public static readonly string ArrowAltCircleUp = "fas fa-arrow-alt-circle-up fa-fw"; - public static readonly string ArrowCircleDown = "fas fa-arrow-circle-down fa-fw"; - public static readonly string ArrowCircleLeft = "fas fa-arrow-circle-left fa-fw"; - public static readonly string ArrowCircleRight = "fas fa-arrow-circle-right fa-fw"; - public static readonly string ArrowCircleUp = "fas fa-arrow-circle-up fa-fw"; - public static readonly string ArrowDown = "fas fa-arrow-down fa-fw"; - public static readonly string ArrowLeft = "fas fa-arrow-left fa-fw"; - public static readonly string ArrowRight = "fas fa-arrow-right fa-fw"; - public static readonly string ArrowUp = "fas fa-arrow-up fa-fw"; - public static readonly string ArrowsAlt = "fas fa-arrows-alt fa-fw"; - public static readonly string ArrowsAltH = "fas fa-arrows-alt-h fa-fw"; - public static readonly string ArrowsAltV = "fas fa-arrows-alt-v fa-fw"; - public static readonly string AssistiveListeningSystems = "fas fa-assistive-listening-systems fa-fw"; - public static readonly string Asterisk = "fas fa-asterisk fa-fw"; - public static readonly string At = "fas fa-at fa-fw"; - public static readonly string Atlas = "fas fa-atlas fa-fw"; - public static readonly string Atom = "fas fa-atom fa-fw"; - public static readonly string AudioDescription = "fas fa-audio-description fa-fw"; - public static readonly string Award = "fas fa-award fa-fw"; - public static readonly string Baby = "fas fa-baby fa-fw"; - public static readonly string BabyCarriage = "fas fa-baby-carriage fa-fw"; - public static readonly string Backspace = "fas fa-backspace fa-fw"; - public static readonly string Backward = "fas fa-backward fa-fw"; - public static readonly string Bacon = "fas fa-bacon fa-fw"; - public static readonly string BalanceScale = "fas fa-balance-scale fa-fw"; - public static readonly string BalanceScaleLeft = "fas fa-balance-scale-left fa-fw"; - public static readonly string BalanceScaleRight = "fas fa-balance-scale-right fa-fw"; - public static readonly string Ban = "fas fa-ban fa-fw"; - public static readonly string BandAid = "fas fa-band-aid fa-fw"; - public static readonly string Barcode = "fas fa-barcode fa-fw"; - public static readonly string Bars = "fas fa-bars fa-fw"; - public static readonly string BaseballBall = "fas fa-baseball-ball fa-fw"; - public static readonly string BasketballBall = "fas fa-basketball-ball fa-fw"; - public static readonly string Bath = "fas fa-bath fa-fw"; - public static readonly string BatteryEmpty = "fas fa-battery-empty fa-fw"; - public static readonly string BatteryFull = "fas fa-battery-full fa-fw"; - public static readonly string BatteryHalf = "fas fa-battery-half fa-fw"; - public static readonly string BatteryQuarter = "fas fa-battery-quarter fa-fw"; - public static readonly string BatteryThreeQuarters = "fas fa-battery-three-quarters fa-fw"; - public static readonly string Bed = "fas fa-bed fa-fw"; - public static readonly string Beer = "fas fa-beer fa-fw"; - public static readonly string Bell = "fas fa-bell fa-fw"; - public static readonly string BellSlash = "fas fa-bell-slash fa-fw"; - public static readonly string BezierCurve = "fas fa-bezier-curve fa-fw"; - public static readonly string Bible = "fas fa-bible fa-fw"; - public static readonly string Bicycle = "fas fa-bicycle fa-fw"; - public static readonly string Biking = "fas fa-biking fa-fw"; - public static readonly string Binoculars = "fas fa-binoculars fa-fw"; - public static readonly string Biohazard = "fas fa-biohazard fa-fw"; - public static readonly string BirthdayCake = "fas fa-birthday-cake fa-fw"; - public static readonly string Blender = "fas fa-blender fa-fw"; - public static readonly string BlenderPhone = "fas fa-blender-phone fa-fw"; - public static readonly string Blind = "fas fa-blind fa-fw"; - public static readonly string Blog = "fas fa-blog fa-fw"; - public static readonly string Bold = "fas fa-bold fa-fw"; - public static readonly string Bolt = "fas fa-bolt fa-fw"; - public static readonly string Bomb = "fas fa-bomb fa-fw"; - public static readonly string Bone = "fas fa-bone fa-fw"; - public static readonly string Bong = "fas fa-bong fa-fw"; - public static readonly string Book = "fas fa-book fa-fw"; - public static readonly string BookDead = "fas fa-book-dead fa-fw"; - public static readonly string BookMedical = "fas fa-book-medical fa-fw"; - public static readonly string BookOpen = "fas fa-book-open fa-fw"; - public static readonly string BookReader = "fas fa-book-reader fa-fw"; - public static readonly string Bookmark = "fas fa-bookmark fa-fw"; - public static readonly string BorderAll = "fas fa-border-all fa-fw"; - public static readonly string BorderNone = "fas fa-border-none fa-fw"; - public static readonly string BorderStyle = "fas fa-border-style fa-fw"; - public static readonly string BowlingBall = "fas fa-bowling-ball fa-fw"; - public static readonly string Box = "fas fa-box fa-fw"; - public static readonly string BoxOpen = "fas fa-box-open fa-fw"; - public static readonly string Boxes = "fas fa-boxes fa-fw"; - public static readonly string Braille = "fas fa-braille fa-fw"; - public static readonly string Brain = "fas fa-brain fa-fw"; - public static readonly string BreadSlice = "fas fa-bread-slice fa-fw"; - public static readonly string Briefcase = "fas fa-briefcase fa-fw"; - public static readonly string BriefcaseMedical = "fas fa-briefcase-medical fa-fw"; - public static readonly string BroadcastTower = "fas fa-broadcast-tower fa-fw"; - public static readonly string Broom = "fas fa-broom fa-fw"; - public static readonly string Brush = "fas fa-brush fa-fw"; - public static readonly string Bug = "fas fa-bug fa-fw"; - public static readonly string Building = "fas fa-building fa-fw"; - public static readonly string Bullhorn = "fas fa-bullhorn fa-fw"; - public static readonly string Bullseye = "fas fa-bullseye fa-fw"; - public static readonly string Burn = "fas fa-burn fa-fw"; - public static readonly string Bus = "fas fa-bus fa-fw"; - public static readonly string BusAlt = "fas fa-bus-alt fa-fw"; - public static readonly string BusinessTime = "fas fa-business-time fa-fw"; - public static readonly string Calculator = "fas fa-calculator fa-fw"; - public static readonly string Calendar = "fas fa-calendar fa-fw"; - public static readonly string CalendarAlt = "fas fa-calendar-alt fa-fw"; - public static readonly string CalendarCheck = "fas fa-calendar-check fa-fw"; - public static readonly string CalendarDay = "fas fa-calendar-day fa-fw"; - public static readonly string CalendarMinus = "fas fa-calendar-minus fa-fw"; - public static readonly string CalendarPlus = "fas fa-calendar-plus fa-fw"; - public static readonly string CalendarTimes = "fas fa-calendar-times fa-fw"; - public static readonly string CalendarWeek = "fas fa-calendar-week fa-fw"; - public static readonly string Camera = "fas fa-camera fa-fw"; - public static readonly string CameraRetro = "fas fa-camera-retro fa-fw"; - public static readonly string Campground = "fas fa-campground fa-fw"; - public static readonly string CandyCane = "fas fa-candy-cane fa-fw"; - public static readonly string Cannabis = "fas fa-cannabis fa-fw"; - public static readonly string Capsules = "fas fa-capsules fa-fw"; - public static readonly string Car = "fas fa-car fa-fw"; - public static readonly string CarAlt = "fas fa-car-alt fa-fw"; - public static readonly string CarBattery = "fas fa-car-battery fa-fw"; - public static readonly string CarCrash = "fas fa-car-crash fa-fw"; - public static readonly string CarSide = "fas fa-car-side fa-fw"; - public static readonly string CaretDown = "fas fa-caret-down fa-fw"; - public static readonly string CaretLeft = "fas fa-caret-left fa-fw"; - public static readonly string CaretRight = "fas fa-caret-right fa-fw"; - public static readonly string CaretSquareDown = "fas fa-caret-square-down fa-fw"; - public static readonly string CaretSquareLeft = "fas fa-caret-square-left fa-fw"; - public static readonly string CaretSquareRight = "fas fa-caret-square-right fa-fw"; - public static readonly string CaretSquareUp = "fas fa-caret-square-up fa-fw"; - public static readonly string CaretUp = "fas fa-caret-up fa-fw"; - public static readonly string Carrot = "fas fa-carrot fa-fw"; - public static readonly string CartArrowDown = "fas fa-cart-arrow-down fa-fw"; - public static readonly string CartPlus = "fas fa-cart-plus fa-fw"; - public static readonly string CashRegister = "fas fa-cash-register fa-fw"; - public static readonly string Cat = "fas fa-cat fa-fw"; - public static readonly string Certificate = "fas fa-certificate fa-fw"; - public static readonly string Chair = "fas fa-chair fa-fw"; - public static readonly string Chalkboard = "fas fa-chalkboard fa-fw"; - public static readonly string ChalkboardTeacher = "fas fa-chalkboard-teacher fa-fw"; - public static readonly string ChargingStation = "fas fa-charging-station fa-fw"; - public static readonly string ChartArea = "fas fa-chart-area fa-fw"; - public static readonly string ChartBar = "fas fa-chart-bar fa-fw"; - public static readonly string ChartLine = "fas fa-chart-line fa-fw"; - public static readonly string ChartPie = "fas fa-chart-pie fa-fw"; - public static readonly string Check = "fas fa-check fa-fw"; - public static readonly string CheckCircle = "fas fa-check-circle fa-fw"; - public static readonly string CheckDouble = "fas fa-check-double fa-fw"; - public static readonly string CheckSquare = "fas fa-check-square fa-fw"; - public static readonly string Cheese = "fas fa-cheese fa-fw"; - public static readonly string Chess = "fas fa-chess fa-fw"; - public static readonly string ChessBishop = "fas fa-chess-bishop fa-fw"; - public static readonly string ChessBoard = "fas fa-chess-board fa-fw"; - public static readonly string ChessKing = "fas fa-chess-king fa-fw"; - public static readonly string ChessKnight = "fas fa-chess-knight fa-fw"; - public static readonly string ChessPawn = "fas fa-chess-pawn fa-fw"; - public static readonly string ChessQueen = "fas fa-chess-queen fa-fw"; - public static readonly string ChessRook = "fas fa-chess-rook fa-fw"; - public static readonly string ChevronCircleDown = "fas fa-chevron-circle-down fa-fw"; - public static readonly string ChevronCircleLeft = "fas fa-chevron-circle-left fa-fw"; - public static readonly string ChevronCircleRight = "fas fa-chevron-circle-right fa-fw"; - public static readonly string ChevronCircleUp = "fas fa-chevron-circle-up fa-fw"; - public static readonly string ChevronDown = "fas fa-chevron-down fa-fw"; - public static readonly string ChevronLeft = "fas fa-chevron-left fa-fw"; - public static readonly string ChevronRight = "fas fa-chevron-right fa-fw"; - public static readonly string ChevronUp = "fas fa-chevron-up fa-fw"; - public static readonly string Child = "fas fa-child fa-fw"; - public static readonly string Church = "fas fa-church fa-fw"; - public static readonly string Circle = "fas fa-circle fa-fw"; - public static readonly string CircleNotch = "fas fa-circle-notch fa-fw"; - public static readonly string City = "fas fa-city fa-fw"; - public static readonly string ClinicMedical = "fas fa-clinic-medical fa-fw"; - public static readonly string Clipboard = "fas fa-clipboard fa-fw"; - public static readonly string ClipboardCheck = "fas fa-clipboard-check fa-fw"; - public static readonly string ClipboardList = "fas fa-clipboard-list fa-fw"; - public static readonly string Clock = "fas fa-clock fa-fw"; - public static readonly string Clone = "fas fa-clone fa-fw"; - public static readonly string ClosedCaptioning = "fas fa-closed-captioning fa-fw"; - public static readonly string Cloud = "fas fa-cloud fa-fw"; - public static readonly string CloudDownloadAlt = "fas fa-cloud-download-alt fa-fw"; - public static readonly string CloudMeatball = "fas fa-cloud-meatball fa-fw"; - public static readonly string CloudMoon = "fas fa-cloud-moon fa-fw"; - public static readonly string CloudMoonRain = "fas fa-cloud-moon-rain fa-fw"; - public static readonly string CloudRain = "fas fa-cloud-rain fa-fw"; - public static readonly string CloudShowersHeavy = "fas fa-cloud-showers-heavy fa-fw"; - public static readonly string CloudSun = "fas fa-cloud-sun fa-fw"; - public static readonly string CloudSunRain = "fas fa-cloud-sun-rain fa-fw"; - public static readonly string CloudUploadAlt = "fas fa-cloud-upload-alt fa-fw"; - public static readonly string Cocktail = "fas fa-cocktail fa-fw"; - public static readonly string Code = "fas fa-code fa-fw"; - public static readonly string CodeBranch = "fas fa-code-branch fa-fw"; - public static readonly string Coffee = "fas fa-coffee fa-fw"; - public static readonly string Cog = "fas fa-cog fa-fw"; - public static readonly string Cogs = "fas fa-cogs fa-fw"; - public static readonly string Coins = "fas fa-coins fa-fw"; - public static readonly string Columns = "fas fa-columns fa-fw"; - public static readonly string Comment = "fas fa-comment fa-fw"; - public static readonly string CommentAlt = "fas fa-comment-alt fa-fw"; - public static readonly string CommentDollar = "fas fa-comment-dollar fa-fw"; - public static readonly string CommentDots = "fas fa-comment-dots fa-fw"; - public static readonly string CommentMedical = "fas fa-comment-medical fa-fw"; - public static readonly string CommentSlash = "fas fa-comment-slash fa-fw"; - public static readonly string Comments = "fas fa-comments fa-fw"; - public static readonly string CommentsDollar = "fas fa-comments-dollar fa-fw"; - public static readonly string CompactDisc = "fas fa-compact-disc fa-fw"; - public static readonly string Compass = "fas fa-compass fa-fw"; - public static readonly string Compress = "fas fa-compress fa-fw"; - public static readonly string CompressArrowsAlt = "fas fa-compress-arrows-alt fa-fw"; - public static readonly string ConciergeBell = "fas fa-concierge-bell fa-fw"; - public static readonly string Cookie = "fas fa-cookie fa-fw"; - public static readonly string CookieBite = "fas fa-cookie-bite fa-fw"; - public static readonly string Copy = "fas fa-copy fa-fw"; - public static readonly string Copyright = "fas fa-copyright fa-fw"; - public static readonly string Couch = "fas fa-couch fa-fw"; - public static readonly string CreditCard = "fas fa-credit-card fa-fw"; - public static readonly string Crop = "fas fa-crop fa-fw"; - public static readonly string CropAlt = "fas fa-crop-alt fa-fw"; - public static readonly string Cross = "fas fa-cross fa-fw"; - public static readonly string Crosshairs = "fas fa-crosshairs fa-fw"; - public static readonly string Crow = "fas fa-crow fa-fw"; - public static readonly string Crown = "fas fa-crown fa-fw"; - public static readonly string Crutch = "fas fa-crutch fa-fw"; - public static readonly string Cube = "fas fa-cube fa-fw"; - public static readonly string Cubes = "fas fa-cubes fa-fw"; - public static readonly string Cut = "fas fa-cut fa-fw"; - public static readonly string Database = "fas fa-database fa-fw"; - public static readonly string Deaf = "fas fa-deaf fa-fw"; - public static readonly string Democrat = "fas fa-democrat fa-fw"; - public static readonly string Desktop = "fas fa-desktop fa-fw"; - public static readonly string Dharmachakra = "fas fa-dharmachakra fa-fw"; - public static readonly string Diagnoses = "fas fa-diagnoses fa-fw"; - public static readonly string Dice = "fas fa-dice fa-fw"; - public static readonly string DiceD20 = "fas fa-dice-d20 fa-fw"; - public static readonly string DiceD6 = "fas fa-dice-d6 fa-fw"; - public static readonly string DiceFive = "fas fa-dice-five fa-fw"; - public static readonly string DiceFour = "fas fa-dice-four fa-fw"; - public static readonly string DiceOne = "fas fa-dice-one fa-fw"; - public static readonly string DiceSix = "fas fa-dice-six fa-fw"; - public static readonly string DiceThree = "fas fa-dice-three fa-fw"; - public static readonly string DiceTwo = "fas fa-dice-two fa-fw"; - public static readonly string DigitalTachograph = "fas fa-digital-tachograph fa-fw"; - public static readonly string Directions = "fas fa-directions fa-fw"; - public static readonly string Divide = "fas fa-divide fa-fw"; - public static readonly string Dizzy = "fas fa-dizzy fa-fw"; - public static readonly string Dna = "fas fa-dna fa-fw"; - public static readonly string Dog = "fas fa-dog fa-fw"; - public static readonly string DollarSign = "fas fa-dollar-sign fa-fw"; - public static readonly string Dolly = "fas fa-dolly fa-fw"; - public static readonly string DollyFlatbed = "fas fa-dolly-flatbed fa-fw"; - public static readonly string Donate = "fas fa-donate fa-fw"; - public static readonly string DoorClosed = "fas fa-door-closed fa-fw"; - public static readonly string DoorOpen = "fas fa-door-open fa-fw"; - public static readonly string DotCircle = "fas fa-dot-circle fa-fw"; - public static readonly string Dove = "fas fa-dove fa-fw"; - public static readonly string Download = "fas fa-download fa-fw"; - public static readonly string DraftingCompass = "fas fa-drafting-compass fa-fw"; - public static readonly string Dragon = "fas fa-dragon fa-fw"; - public static readonly string DrawPolygon = "fas fa-draw-polygon fa-fw"; - public static readonly string Drum = "fas fa-drum fa-fw"; - public static readonly string DrumSteelpan = "fas fa-drum-steelpan fa-fw"; - public static readonly string DrumstickBite = "fas fa-drumstick-bite fa-fw"; - public static readonly string Dumbbell = "fas fa-dumbbell fa-fw"; - public static readonly string Dumpster = "fas fa-dumpster fa-fw"; - public static readonly string DumpsterFire = "fas fa-dumpster-fire fa-fw"; - public static readonly string Dungeon = "fas fa-dungeon fa-fw"; - public static readonly string Edit = "fas fa-edit fa-fw"; - public static readonly string Egg = "fas fa-egg fa-fw"; - public static readonly string Eject = "fas fa-eject fa-fw"; - public static readonly string EllipsisH = "fas fa-ellipsis-h fa-fw"; - public static readonly string EllipsisV = "fas fa-ellipsis-v fa-fw"; - public static readonly string Envelope = "fas fa-envelope fa-fw"; - public static readonly string EnvelopeOpen = "fas fa-envelope-open fa-fw"; - public static readonly string EnvelopeOpenText = "fas fa-envelope-open-text fa-fw"; - public static readonly string EnvelopeSquare = "fas fa-envelope-square fa-fw"; - public static readonly new string Equals = "fas fa-equals fa-fw"; - public static readonly string Eraser = "fas fa-eraser fa-fw"; - public static readonly string Ethernet = "fas fa-ethernet fa-fw"; - public static readonly string EuroSign = "fas fa-euro-sign fa-fw"; - public static readonly string ExchangeAlt = "fas fa-exchange-alt fa-fw"; - public static readonly string Exclamation = "fas fa-exclamation fa-fw"; - public static readonly string ExclamationCircle = "fas fa-exclamation-circle fa-fw"; - public static readonly string ExclamationTriangle = "fas fa-exclamation-triangle fa-fw"; - public static readonly string Expand = "fas fa-expand fa-fw"; - public static readonly string ExpandArrowsAlt = "fas fa-expand-arrows-alt fa-fw"; - public static readonly string ExternalLinkAlt = "fas fa-external-link-alt fa-fw"; - public static readonly string ExternalLinkSquareAlt = "fas fa-external-link-square-alt fa-fw"; - public static readonly string Eye = "fas fa-eye fa-fw"; - public static readonly string EyeDropper = "fas fa-eye-dropper fa-fw"; - public static readonly string EyeSlash = "fas fa-eye-slash fa-fw"; - public static readonly string Fan = "fas fa-fan fa-fw"; - public static readonly string FastBackward = "fas fa-fast-backward fa-fw"; - public static readonly string FastForward = "fas fa-fast-forward fa-fw"; - public static readonly string Fax = "fas fa-fax fa-fw"; - public static readonly string Feather = "fas fa-feather fa-fw"; - public static readonly string FeatherAlt = "fas fa-feather-alt fa-fw"; - public static readonly string Female = "fas fa-female fa-fw"; - public static readonly string FighterJet = "fas fa-fighter-jet fa-fw"; - public static readonly string File = "fas fa-file fa-fw"; - public static readonly string FileAlt = "fas fa-file-alt fa-fw"; - public static readonly string FileArchive = "fas fa-file-archive fa-fw"; - public static readonly string FileAudio = "fas fa-file-audio fa-fw"; - public static readonly string FileCode = "fas fa-file-code fa-fw"; - public static readonly string FileContract = "fas fa-file-contract fa-fw"; - public static readonly string FileCsv = "fas fa-file-csv fa-fw"; - public static readonly string FileDownload = "fas fa-file-download fa-fw"; - public static readonly string FileExcel = "fas fa-file-excel fa-fw"; - public static readonly string FileExport = "fas fa-file-export fa-fw"; - public static readonly string FileImage = "fas fa-file-image fa-fw"; - public static readonly string FileImport = "fas fa-file-import fa-fw"; - public static readonly string FileInvoice = "fas fa-file-invoice fa-fw"; - public static readonly string FileInvoiceDollar = "fas fa-file-invoice-dollar fa-fw"; - public static readonly string FileMedical = "fas fa-file-medical fa-fw"; - public static readonly string FileMedicalAlt = "fas fa-file-medical-alt fa-fw"; - public static readonly string FilePdf = "fas fa-file-pdf fa-fw"; - public static readonly string FilePowerpoint = "fas fa-file-powerpoint fa-fw"; - public static readonly string FilePrescription = "fas fa-file-prescription fa-fw"; - public static readonly string FileSignature = "fas fa-file-signature fa-fw"; - public static readonly string FileUpload = "fas fa-file-upload fa-fw"; - public static readonly string FileVideo = "fas fa-file-video fa-fw"; - public static readonly string FileWord = "fas fa-file-word fa-fw"; - public static readonly string Fill = "fas fa-fill fa-fw"; - public static readonly string FillDrip = "fas fa-fill-drip fa-fw"; - public static readonly string Film = "fas fa-film fa-fw"; - public static readonly string Filter = "fas fa-filter fa-fw"; - public static readonly string Fingerprint = "fas fa-fingerprint fa-fw"; - public static readonly string Fire = "fas fa-fire fa-fw"; - public static readonly string FireAlt = "fas fa-fire-alt fa-fw"; - public static readonly string FireExtinguisher = "fas fa-fire-extinguisher fa-fw"; - public static readonly string FirstAid = "fas fa-first-aid fa-fw"; - public static readonly string Fish = "fas fa-fish fa-fw"; - public static readonly string FistRaised = "fas fa-fist-raised fa-fw"; - public static readonly string Flag = "fas fa-flag fa-fw"; - public static readonly string FlagCheckered = "fas fa-flag-checkered fa-fw"; - public static readonly string FlagUsa = "fas fa-flag-usa fa-fw"; - public static readonly string Flask = "fas fa-flask fa-fw"; - public static readonly string Flushed = "fas fa-flushed fa-fw"; - public static readonly string Folder = "fas fa-folder fa-fw"; - public static readonly string FolderMinus = "fas fa-folder-minus fa-fw"; - public static readonly string FolderOpen = "fas fa-folder-open fa-fw"; - public static readonly string FolderPlus = "fas fa-folder-plus fa-fw"; - public static readonly string Font = "fas fa-font fa-fw"; - public static readonly string FootballBall = "fas fa-football-ball fa-fw"; - public static readonly string Forward = "fas fa-forward fa-fw"; - public static readonly string Frog = "fas fa-frog fa-fw"; - public static readonly string Frown = "fas fa-frown fa-fw"; - public static readonly string FrownOpen = "fas fa-frown-open fa-fw"; - public static readonly string FunnelDollar = "fas fa-funnel-dollar fa-fw"; - public static readonly string Futbol = "fas fa-futbol fa-fw"; - public static readonly string Gamepad = "fas fa-gamepad fa-fw"; - public static readonly string GasPump = "fas fa-gas-pump fa-fw"; - public static readonly string Gavel = "fas fa-gavel fa-fw"; - public static readonly string Gem = "fas fa-gem fa-fw"; - public static readonly string Genderless = "fas fa-genderless fa-fw"; - public static readonly string Ghost = "fas fa-ghost fa-fw"; - public static readonly string Gift = "fas fa-gift fa-fw"; - public static readonly string Gifts = "fas fa-gifts fa-fw"; - public static readonly string GlassCheers = "fas fa-glass-cheers fa-fw"; - public static readonly string GlassMartini = "fas fa-glass-martini fa-fw"; - public static readonly string GlassMartiniAlt = "fas fa-glass-martini-alt fa-fw"; - public static readonly string GlassWhiskey = "fas fa-glass-whiskey fa-fw"; - public static readonly string Glasses = "fas fa-glasses fa-fw"; - public static readonly string Globe = "fas fa-globe fa-fw"; - public static readonly string GlobeAfrica = "fas fa-globe-africa fa-fw"; - public static readonly string GlobeAmericas = "fas fa-globe-americas fa-fw"; - public static readonly string GlobeAsia = "fas fa-globe-asia fa-fw"; - public static readonly string GlobeEurope = "fas fa-globe-europe fa-fw"; - public static readonly string GolfBall = "fas fa-golf-ball fa-fw"; - public static readonly string Gopuram = "fas fa-gopuram fa-fw"; - public static readonly string GraduationCap = "fas fa-graduation-cap fa-fw"; - public static readonly string GreaterThan = "fas fa-greater-than fa-fw"; - public static readonly string GreaterThanEqual = "fas fa-greater-than-equal fa-fw"; - public static readonly string Grimace = "fas fa-grimace fa-fw"; - public static readonly string Grin = "fas fa-grin fa-fw"; - public static readonly string GrinAlt = "fas fa-grin-alt fa-fw"; - public static readonly string GrinBeam = "fas fa-grin-beam fa-fw"; - public static readonly string GrinBeamSweat = "fas fa-grin-beam-sweat fa-fw"; - public static readonly string GrinHearts = "fas fa-grin-hearts fa-fw"; - public static readonly string GrinSquint = "fas fa-grin-squint fa-fw"; - public static readonly string GrinSquintTears = "fas fa-grin-squint-tears fa-fw"; - public static readonly string GrinStars = "fas fa-grin-stars fa-fw"; - public static readonly string GrinTears = "fas fa-grin-tears fa-fw"; - public static readonly string GrinTongue = "fas fa-grin-tongue fa-fw"; - public static readonly string GrinTongueSquint = "fas fa-grin-tongue-squint fa-fw"; - public static readonly string GrinTongueWink = "fas fa-grin-tongue-wink fa-fw"; - public static readonly string GrinWink = "fas fa-grin-wink fa-fw"; - public static readonly string GripHorizontal = "fas fa-grip-horizontal fa-fw"; - public static readonly string GripLines = "fas fa-grip-lines fa-fw"; - public static readonly string GripLinesVertical = "fas fa-grip-lines-vertical fa-fw"; - public static readonly string GripVertical = "fas fa-grip-vertical fa-fw"; - public static readonly string Guitar = "fas fa-guitar fa-fw"; - public static readonly string HSquare = "fas fa-h-square fa-fw"; - public static readonly string Hamburger = "fas fa-hamburger fa-fw"; - public static readonly string Hammer = "fas fa-hammer fa-fw"; - public static readonly string Hamsa = "fas fa-hamsa fa-fw"; - public static readonly string HandHolding = "fas fa-hand-holding fa-fw"; - public static readonly string HandHoldingHeart = "fas fa-hand-holding-heart fa-fw"; - public static readonly string HandHoldingUsd = "fas fa-hand-holding-usd fa-fw"; - public static readonly string HandLizard = "fas fa-hand-lizard fa-fw"; - public static readonly string HandMiddleFinger = "fas fa-hand-middle-finger fa-fw"; - public static readonly string HandPaper = "fas fa-hand-paper fa-fw"; - public static readonly string HandPeace = "fas fa-hand-peace fa-fw"; - public static readonly string HandPointDown = "fas fa-hand-point-down fa-fw"; - public static readonly string HandPointLeft = "fas fa-hand-point-left fa-fw"; - public static readonly string HandPointRight = "fas fa-hand-point-right fa-fw"; - public static readonly string HandPointUp = "fas fa-hand-point-up fa-fw"; - public static readonly string HandPointer = "fas fa-hand-pointer fa-fw"; - public static readonly string HandRock = "fas fa-hand-rock fa-fw"; - public static readonly string HandScissors = "fas fa-hand-scissors fa-fw"; - public static readonly string HandSpock = "fas fa-hand-spock fa-fw"; - public static readonly string Hands = "fas fa-hands fa-fw"; - public static readonly string HandsHelping = "fas fa-hands-helping fa-fw"; - public static readonly string Handshake = "fas fa-handshake fa-fw"; - public static readonly string Hanukiah = "fas fa-hanukiah fa-fw"; - public static readonly string HardHat = "fas fa-hard-hat fa-fw"; - public static readonly string Hashtag = "fas fa-hashtag fa-fw"; - public static readonly string HatCowboy = "fas fa-hat-cowboy fa-fw"; - public static readonly string HatCowboySide = "fas fa-hat-cowboy-side fa-fw"; - public static readonly string HatWizard = "fas fa-hat-wizard fa-fw"; - public static readonly string Haykal = "fas fa-haykal fa-fw"; - public static readonly string Hdd = "fas fa-hdd fa-fw"; - public static readonly string Heading = "fas fa-heading fa-fw"; - public static readonly string Headphones = "fas fa-headphones fa-fw"; - public static readonly string HeadphonesAlt = "fas fa-headphones-alt fa-fw"; - public static readonly string Headset = "fas fa-headset fa-fw"; - public static readonly string Heart = "fas fa-heart fa-fw"; - public static readonly string HeartBroken = "fas fa-heart-broken fa-fw"; - public static readonly string Heartbeat = "fas fa-heartbeat fa-fw"; - public static readonly string Helicopter = "fas fa-helicopter fa-fw"; - public static readonly string Highlighter = "fas fa-highlighter fa-fw"; - public static readonly string Hiking = "fas fa-hiking fa-fw"; - public static readonly string Hippo = "fas fa-hippo fa-fw"; - public static readonly string History = "fas fa-history fa-fw"; - public static readonly string HockeyPuck = "fas fa-hockey-puck fa-fw"; - public static readonly string HollyBerry = "fas fa-holly-berry fa-fw"; - public static readonly string Home = "fas fa-home fa-fw"; - public static readonly string Horse = "fas fa-horse fa-fw"; - public static readonly string HorseHead = "fas fa-horse-head fa-fw"; - public static readonly string Hospital = "fas fa-hospital fa-fw"; - public static readonly string HospitalAlt = "fas fa-hospital-alt fa-fw"; - public static readonly string HospitalSymbol = "fas fa-hospital-symbol fa-fw"; - public static readonly string HotTub = "fas fa-hot-tub fa-fw"; - public static readonly string Hotdog = "fas fa-hotdog fa-fw"; - public static readonly string Hotel = "fas fa-hotel fa-fw"; - public static readonly string Hourglass = "fas fa-hourglass fa-fw"; - public static readonly string HourglassEnd = "fas fa-hourglass-end fa-fw"; - public static readonly string HourglassHalf = "fas fa-hourglass-half fa-fw"; - public static readonly string HourglassStart = "fas fa-hourglass-start fa-fw"; - public static readonly string HouseDamage = "fas fa-house-damage fa-fw"; - public static readonly string Hryvnia = "fas fa-hryvnia fa-fw"; - public static readonly string ICursor = "fas fa-i-cursor fa-fw"; - public static readonly string IceCream = "fas fa-ice-cream fa-fw"; - public static readonly string Icicles = "fas fa-icicles fa-fw"; - public static readonly string Icons = "fas fa-icons fa-fw"; - public static readonly string IdBadge = "fas fa-id-badge fa-fw"; - public static readonly string IdCard = "fas fa-id-card fa-fw"; - public static readonly string IdCardAlt = "fas fa-id-card-alt fa-fw"; - public static readonly string Igloo = "fas fa-igloo fa-fw"; - public static readonly string Image = "fas fa-image fa-fw"; - public static readonly string Images = "fas fa-images fa-fw"; - public static readonly string Inbox = "fas fa-inbox fa-fw"; - public static readonly string Indent = "fas fa-indent fa-fw"; - public static readonly string Industry = "fas fa-industry fa-fw"; - public static readonly string Infinity = "fas fa-infinity fa-fw"; - public static readonly string Info = "fas fa-info fa-fw"; - public static readonly string InfoCircle = "fas fa-info-circle fa-fw"; - public static readonly string Italic = "fas fa-italic fa-fw"; - public static readonly string Jedi = "fas fa-jedi fa-fw"; - public static readonly string Joint = "fas fa-joint fa-fw"; - public static readonly string JournalWhills = "fas fa-journal-whills fa-fw"; - public static readonly string Kaaba = "fas fa-kaaba fa-fw"; - public static readonly string Key = "fas fa-key fa-fw"; - public static readonly string Keyboard = "fas fa-keyboard fa-fw"; - public static readonly string Khanda = "fas fa-khanda fa-fw"; - public static readonly string Kiss = "fas fa-kiss fa-fw"; - public static readonly string KissBeam = "fas fa-kiss-beam fa-fw"; - public static readonly string KissWinkHeart = "fas fa-kiss-wink-heart fa-fw"; - public static readonly string KiwiBird = "fas fa-kiwi-bird fa-fw"; - public static readonly string Landmark = "fas fa-landmark fa-fw"; - public static readonly string Language = "fas fa-language fa-fw"; - public static readonly string Laptop = "fas fa-laptop fa-fw"; - public static readonly string LaptopCode = "fas fa-laptop-code fa-fw"; - public static readonly string LaptopMedical = "fas fa-laptop-medical fa-fw"; - public static readonly string Laugh = "fas fa-laugh fa-fw"; - public static readonly string LaughBeam = "fas fa-laugh-beam fa-fw"; - public static readonly string LaughSquint = "fas fa-laugh-squint fa-fw"; - public static readonly string LaughWink = "fas fa-laugh-wink fa-fw"; - public static readonly string LayerGroup = "fas fa-layer-group fa-fw"; - public static readonly string Leaf = "fas fa-leaf fa-fw"; - public static readonly string Lemon = "fas fa-lemon fa-fw"; - public static readonly string LessThan = "fas fa-less-than fa-fw"; - public static readonly string LessThanEqual = "fas fa-less-than-equal fa-fw"; - public static readonly string LevelDownAlt = "fas fa-level-down-alt fa-fw"; - public static readonly string LevelUpAlt = "fas fa-level-up-alt fa-fw"; - public static readonly string LifeRing = "fas fa-life-ring fa-fw"; - public static readonly string Lightbulb = "fas fa-lightbulb fa-fw"; - public static readonly string Link = "fas fa-link fa-fw"; - public static readonly string LiraSign = "fas fa-lira-sign fa-fw"; - public static readonly string List = "fas fa-list fa-fw"; - public static readonly string ListAlt = "fas fa-list-alt fa-fw"; - public static readonly string ListOl = "fas fa-list-ol fa-fw"; - public static readonly string ListUl = "fas fa-list-ul fa-fw"; - public static readonly string LocationArrow = "fas fa-location-arrow fa-fw"; - public static readonly string Lock = "fas fa-lock fa-fw"; - public static readonly string LockOpen = "fas fa-lock-open fa-fw"; - public static readonly string LongArrowAltDown = "fas fa-long-arrow-alt-down fa-fw"; - public static readonly string LongArrowAltLeft = "fas fa-long-arrow-alt-left fa-fw"; - public static readonly string LongArrowAltRight = "fas fa-long-arrow-alt-right fa-fw"; - public static readonly string LongArrowAltUp = "fas fa-long-arrow-alt-up fa-fw"; - public static readonly string LowVision = "fas fa-low-vision fa-fw"; - public static readonly string LuggageCart = "fas fa-luggage-cart fa-fw"; - public static readonly string Magic = "fas fa-magic fa-fw"; - public static readonly string Magnet = "fas fa-magnet fa-fw"; - public static readonly string MailBulk = "fas fa-mail-bulk fa-fw"; - public static readonly string Male = "fas fa-male fa-fw"; - public static readonly string Map = "fas fa-map fa-fw"; - public static readonly string MapMarked = "fas fa-map-marked fa-fw"; - public static readonly string MapMarkedAlt = "fas fa-map-marked-alt fa-fw"; - public static readonly string MapMarker = "fas fa-map-marker fa-fw"; - public static readonly string MapMarkerAlt = "fas fa-map-marker-alt fa-fw"; - public static readonly string MapPin = "fas fa-map-pin fa-fw"; - public static readonly string MapSigns = "fas fa-map-signs fa-fw"; - public static readonly string Marker = "fas fa-marker fa-fw"; - public static readonly string Mars = "fas fa-mars fa-fw"; - public static readonly string MarsDouble = "fas fa-mars-double fa-fw"; - public static readonly string MarsStroke = "fas fa-mars-stroke fa-fw"; - public static readonly string MarsStrokeH = "fas fa-mars-stroke-h fa-fw"; - public static readonly string MarsStrokeV = "fas fa-mars-stroke-v fa-fw"; - public static readonly string Mask = "fas fa-mask fa-fw"; - public static readonly string Medal = "fas fa-medal fa-fw"; - public static readonly string Medkit = "fas fa-medkit fa-fw"; - public static readonly string Meh = "fas fa-meh fa-fw"; - public static readonly string MehBlank = "fas fa-meh-blank fa-fw"; - public static readonly string MehRollingEyes = "fas fa-meh-rolling-eyes fa-fw"; - public static readonly string Memory = "fas fa-memory fa-fw"; - public static readonly string Menorah = "fas fa-menorah fa-fw"; - public static readonly string Mercury = "fas fa-mercury fa-fw"; - public static readonly string Meteor = "fas fa-meteor fa-fw"; - public static readonly string Microchip = "fas fa-microchip fa-fw"; - public static readonly string Microphone = "fas fa-microphone fa-fw"; - public static readonly string MicrophoneAlt = "fas fa-microphone-alt fa-fw"; - public static readonly string MicrophoneAltSlash = "fas fa-microphone-alt-slash fa-fw"; - public static readonly string MicrophoneSlash = "fas fa-microphone-slash fa-fw"; - public static readonly string Microscope = "fas fa-microscope fa-fw"; - public static readonly string Minus = "fas fa-minus fa-fw"; - public static readonly string MinusCircle = "fas fa-minus-circle fa-fw"; - public static readonly string MinusSquare = "fas fa-minus-square fa-fw"; - public static readonly string Mitten = "fas fa-mitten fa-fw"; - public static readonly string Mobile = "fas fa-mobile fa-fw"; - public static readonly string MobileAlt = "fas fa-mobile-alt fa-fw"; - public static readonly string MoneyBill = "fas fa-money-bill fa-fw"; - public static readonly string MoneyBillAlt = "fas fa-money-bill-alt fa-fw"; - public static readonly string MoneyBillWave = "fas fa-money-bill-wave fa-fw"; - public static readonly string MoneyBillWaveAlt = "fas fa-money-bill-wave-alt fa-fw"; - public static readonly string MoneyCheck = "fas fa-money-check fa-fw"; - public static readonly string MoneyCheckAlt = "fas fa-money-check-alt fa-fw"; - public static readonly string Monument = "fas fa-monument fa-fw"; - public static readonly string Moon = "fas fa-moon fa-fw"; - public static readonly string MortarPestle = "fas fa-mortar-pestle fa-fw"; - public static readonly string Mosque = "fas fa-mosque fa-fw"; - public static readonly string Motorcycle = "fas fa-motorcycle fa-fw"; - public static readonly string Mountain = "fas fa-mountain fa-fw"; - public static readonly string Mouse = "fas fa-mouse fa-fw"; - public static readonly string MousePointer = "fas fa-mouse-pointer fa-fw"; - public static readonly string MugHot = "fas fa-mug-hot fa-fw"; - public static readonly string Music = "fas fa-music fa-fw"; - public static readonly string NetworkWired = "fas fa-network-wired fa-fw"; - public static readonly string Neuter = "fas fa-neuter fa-fw"; - public static readonly string Newspaper = "fas fa-newspaper fa-fw"; - public static readonly string NotEqual = "fas fa-not-equal fa-fw"; - public static readonly string NotesMedical = "fas fa-notes-medical fa-fw"; - public static readonly string ObjectGroup = "fas fa-object-group fa-fw"; - public static readonly string ObjectUngroup = "fas fa-object-ungroup fa-fw"; - public static readonly string OilCan = "fas fa-oil-can fa-fw"; - public static readonly string Om = "fas fa-om fa-fw"; - public static readonly string Otter = "fas fa-otter fa-fw"; - public static readonly string Outdent = "fas fa-outdent fa-fw"; - public static readonly string Pager = "fas fa-pager fa-fw"; - public static readonly string PaintBrush = "fas fa-paint-brush fa-fw"; - public static readonly string PaintRoller = "fas fa-paint-roller fa-fw"; - public static readonly string Palette = "fas fa-palette fa-fw"; - public static readonly string Pallet = "fas fa-pallet fa-fw"; - public static readonly string PaperPlane = "fas fa-paper-plane fa-fw"; - public static readonly string Paperclip = "fas fa-paperclip fa-fw"; - public static readonly string ParachuteBox = "fas fa-parachute-box fa-fw"; - public static readonly string Paragraph = "fas fa-paragraph fa-fw"; - public static readonly string Parking = "fas fa-parking fa-fw"; - public static readonly string Passport = "fas fa-passport fa-fw"; - public static readonly string Pastafarianism = "fas fa-pastafarianism fa-fw"; - public static readonly string Paste = "fas fa-paste fa-fw"; - public static readonly string Pause = "fas fa-pause fa-fw"; - public static readonly string PauseCircle = "fas fa-pause-circle fa-fw"; - public static readonly string Paw = "fas fa-paw fa-fw"; - public static readonly string Peace = "fas fa-peace fa-fw"; - public static readonly string Pen = "fas fa-pen fa-fw"; - public static readonly string PenAlt = "fas fa-pen-alt fa-fw"; - public static readonly string PenFancy = "fas fa-pen-fancy fa-fw"; - public static readonly string PenNib = "fas fa-pen-nib fa-fw"; - public static readonly string PenSquare = "fas fa-pen-square fa-fw"; - public static readonly string PencilAlt = "fas fa-pencil-alt fa-fw"; - public static readonly string PencilRuler = "fas fa-pencil-ruler fa-fw"; - public static readonly string PeopleCarry = "fas fa-people-carry fa-fw"; - public static readonly string PepperHot = "fas fa-pepper-hot fa-fw"; - public static readonly string Percent = "fas fa-percent fa-fw"; - public static readonly string Percentage = "fas fa-percentage fa-fw"; - public static readonly string PersonBooth = "fas fa-person-booth fa-fw"; - public static readonly string Phone = "fas fa-phone fa-fw"; - public static readonly string PhoneAlt = "fas fa-phone-alt fa-fw"; - public static readonly string PhoneSlash = "fas fa-phone-slash fa-fw"; - public static readonly string PhoneSquare = "fas fa-phone-square fa-fw"; - public static readonly string PhoneSquareAlt = "fas fa-phone-square-alt fa-fw"; - public static readonly string PhoneVolume = "fas fa-phone-volume fa-fw"; - public static readonly string PhotoVideo = "fas fa-photo-video fa-fw"; - public static readonly string PiggyBank = "fas fa-piggy-bank fa-fw"; - public static readonly string Pills = "fas fa-pills fa-fw"; - public static readonly string PizzaSlice = "fas fa-pizza-slice fa-fw"; - public static readonly string PlaceOfWorship = "fas fa-place-of-worship fa-fw"; - public static readonly string Plane = "fas fa-plane fa-fw"; - public static readonly string PlaneArrival = "fas fa-plane-arrival fa-fw"; - public static readonly string PlaneDeparture = "fas fa-plane-departure fa-fw"; - public static readonly string Play = "fas fa-play fa-fw"; - public static readonly string PlayCircle = "fas fa-play-circle fa-fw"; - public static readonly string Plug = "fas fa-plug fa-fw"; - public static readonly string Plus = "fas fa-plus fa-fw"; - public static readonly string PlusCircle = "fas fa-plus-circle fa-fw"; - public static readonly string PlusSquare = "fas fa-plus-square fa-fw"; - public static readonly string Podcast = "fas fa-podcast fa-fw"; - public static readonly string Poll = "fas fa-poll fa-fw"; - public static readonly string PollH = "fas fa-poll-h fa-fw"; - public static readonly string Poo = "fas fa-poo fa-fw"; - public static readonly string PooStorm = "fas fa-poo-storm fa-fw"; - public static readonly string Poop = "fas fa-poop fa-fw"; - public static readonly string Portrait = "fas fa-portrait fa-fw"; - public static readonly string PoundSign = "fas fa-pound-sign fa-fw"; - public static readonly string PowerOff = "fas fa-power-off fa-fw"; - public static readonly string Pray = "fas fa-pray fa-fw"; - public static readonly string PrayingHands = "fas fa-praying-hands fa-fw"; - public static readonly string Prescription = "fas fa-prescription fa-fw"; - public static readonly string PrescriptionBottle = "fas fa-prescription-bottle fa-fw"; - public static readonly string PrescriptionBottleAlt = "fas fa-prescription-bottle-alt fa-fw"; - public static readonly string Print = "fas fa-print fa-fw"; - public static readonly string Procedures = "fas fa-procedures fa-fw"; - public static readonly string ProjectDiagram = "fas fa-project-diagram fa-fw"; - public static readonly string PuzzlePiece = "fas fa-puzzle-piece fa-fw"; - public static readonly string Qrcode = "fas fa-qrcode fa-fw"; - public static readonly string Question = "fas fa-question fa-fw"; - public static readonly string QuestionCircle = "fas fa-question-circle fa-fw"; - public static readonly string Quidditch = "fas fa-quidditch fa-fw"; - public static readonly string QuoteLeft = "fas fa-quote-left fa-fw"; - public static readonly string QuoteRight = "fas fa-quote-right fa-fw"; - public static readonly string Quran = "fas fa-quran fa-fw"; - public static readonly string Radiation = "fas fa-radiation fa-fw"; - public static readonly string RadiationAlt = "fas fa-radiation-alt fa-fw"; - public static readonly string Rainbow = "fas fa-rainbow fa-fw"; - public static readonly string Random = "fas fa-random fa-fw"; - public static readonly string Receipt = "fas fa-receipt fa-fw"; - public static readonly string RecordVinyl = "fas fa-record-vinyl fa-fw"; - public static readonly string Recycle = "fas fa-recycle fa-fw"; - public static readonly string Redo = "fas fa-redo fa-fw"; - public static readonly string RedoAlt = "fas fa-redo-alt fa-fw"; - public static readonly string Registered = "fas fa-registered fa-fw"; - public static readonly string RemoveFormat = "fas fa-remove-format fa-fw"; - public static readonly string Reply = "fas fa-reply fa-fw"; - public static readonly string ReplyAll = "fas fa-reply-all fa-fw"; - public static readonly string Republican = "fas fa-republican fa-fw"; - public static readonly string Restroom = "fas fa-restroom fa-fw"; - public static readonly string Retweet = "fas fa-retweet fa-fw"; - public static readonly string Ribbon = "fas fa-ribbon fa-fw"; - public static readonly string Ring = "fas fa-ring fa-fw"; - public static readonly string Road = "fas fa-road fa-fw"; - public static readonly string Robot = "fas fa-robot fa-fw"; - public static readonly string Rocket = "fas fa-rocket fa-fw"; - public static readonly string Route = "fas fa-route fa-fw"; - public static readonly string Rss = "fas fa-rss fa-fw"; - public static readonly string RssSquare = "fas fa-rss-square fa-fw"; - public static readonly string RubleSign = "fas fa-ruble-sign fa-fw"; - public static readonly string Ruler = "fas fa-ruler fa-fw"; - public static readonly string RulerCombined = "fas fa-ruler-combined fa-fw"; - public static readonly string RulerHorizontal = "fas fa-ruler-horizontal fa-fw"; - public static readonly string RulerVertical = "fas fa-ruler-vertical fa-fw"; - public static readonly string Running = "fas fa-running fa-fw"; - public static readonly string RupeeSign = "fas fa-rupee-sign fa-fw"; - public static readonly string SadCry = "fas fa-sad-cry fa-fw"; - public static readonly string SadTear = "fas fa-sad-tear fa-fw"; - public static readonly string Satellite = "fas fa-satellite fa-fw"; - public static readonly string SatelliteDish = "fas fa-satellite-dish fa-fw"; - public static readonly string Save = "fas fa-save fa-fw"; - public static readonly string School = "fas fa-school fa-fw"; - public static readonly string Screwdriver = "fas fa-screwdriver fa-fw"; - public static readonly string Scroll = "fas fa-scroll fa-fw"; - public static readonly string SdCard = "fas fa-sd-card fa-fw"; - public static readonly string Search = "fas fa-search fa-fw"; - public static readonly string SearchDollar = "fas fa-search-dollar fa-fw"; - public static readonly string SearchLocation = "fas fa-search-location fa-fw"; - public static readonly string SearchMinus = "fas fa-search-minus fa-fw"; - public static readonly string SearchPlus = "fas fa-search-plus fa-fw"; - public static readonly string Seedling = "fas fa-seedling fa-fw"; - public static readonly string Server = "fas fa-server fa-fw"; - public static readonly string Shapes = "fas fa-shapes fa-fw"; - public static readonly string Share = "fas fa-share fa-fw"; - public static readonly string ShareAlt = "fas fa-share-alt fa-fw"; - public static readonly string ShareAltSquare = "fas fa-share-alt-square fa-fw"; - public static readonly string ShareSquare = "fas fa-share-square fa-fw"; - public static readonly string ShekelSign = "fas fa-shekel-sign fa-fw"; - public static readonly string ShieldAlt = "fas fa-shield-alt fa-fw"; - public static readonly string Ship = "fas fa-ship fa-fw"; - public static readonly string ShippingFast = "fas fa-shipping-fast fa-fw"; - public static readonly string ShoePrints = "fas fa-shoe-prints fa-fw"; - public static readonly string ShoppingBag = "fas fa-shopping-bag fa-fw"; - public static readonly string ShoppingBasket = "fas fa-shopping-basket fa-fw"; - public static readonly string ShoppingCart = "fas fa-shopping-cart fa-fw"; - public static readonly string Shower = "fas fa-shower fa-fw"; - public static readonly string ShuttleVan = "fas fa-shuttle-van fa-fw"; - public static readonly string Sign = "fas fa-sign fa-fw"; - public static readonly string SignInAlt = "fas fa-sign-in-alt fa-fw"; - public static readonly string SignLanguage = "fas fa-sign-language fa-fw"; - public static readonly string SignOutAlt = "fas fa-sign-out-alt fa-fw"; - public static readonly string Signal = "fas fa-signal fa-fw"; - public static readonly string Signature = "fas fa-signature fa-fw"; - public static readonly string SimCard = "fas fa-sim-card fa-fw"; - public static readonly string Sitemap = "fas fa-sitemap fa-fw"; - public static readonly string Skating = "fas fa-skating fa-fw"; - public static readonly string Skiing = "fas fa-skiing fa-fw"; - public static readonly string SkiingNordic = "fas fa-skiing-nordic fa-fw"; - public static readonly string Skull = "fas fa-skull fa-fw"; - public static readonly string SkullCrossbones = "fas fa-skull-crossbones fa-fw"; - public static readonly string Slash = "fas fa-slash fa-fw"; - public static readonly string Sleigh = "fas fa-sleigh fa-fw"; - public static readonly string SlidersH = "fas fa-sliders-h fa-fw"; - public static readonly string Smile = "fas fa-smile fa-fw"; - public static readonly string SmileBeam = "fas fa-smile-beam fa-fw"; - public static readonly string SmileWink = "fas fa-smile-wink fa-fw"; - public static readonly string Smog = "fas fa-smog fa-fw"; - public static readonly string Smoking = "fas fa-smoking fa-fw"; - public static readonly string SmokingBan = "fas fa-smoking-ban fa-fw"; - public static readonly string Sms = "fas fa-sms fa-fw"; - public static readonly string Snowboarding = "fas fa-snowboarding fa-fw"; - public static readonly string Snowflake = "fas fa-snowflake fa-fw"; - public static readonly string Snowman = "fas fa-snowman fa-fw"; - public static readonly string Snowplow = "fas fa-snowplow fa-fw"; - public static readonly string Socks = "fas fa-socks fa-fw"; - public static readonly string SolarPanel = "fas fa-solar-panel fa-fw"; - public static readonly string Sort = "fas fa-sort fa-fw"; - public static readonly string SortAlphaDown = "fas fa-sort-alpha-down fa-fw"; - public static readonly string SortAlphaDownAlt = "fas fa-sort-alpha-down-alt fa-fw"; - public static readonly string SortAlphaUp = "fas fa-sort-alpha-up fa-fw"; - public static readonly string SortAlphaUpAlt = "fas fa-sort-alpha-up-alt fa-fw"; - public static readonly string SortAmountDown = "fas fa-sort-amount-down fa-fw"; - public static readonly string SortAmountDownAlt = "fas fa-sort-amount-down-alt fa-fw"; - public static readonly string SortAmountUp = "fas fa-sort-amount-up fa-fw"; - public static readonly string SortAmountUpAlt = "fas fa-sort-amount-up-alt fa-fw"; - public static readonly string SortDown = "fas fa-sort-down fa-fw"; - public static readonly string SortNumericDown = "fas fa-sort-numeric-down fa-fw"; - public static readonly string SortNumericDownAlt = "fas fa-sort-numeric-down-alt fa-fw"; - public static readonly string SortNumericUp = "fas fa-sort-numeric-up fa-fw"; - public static readonly string SortNumericUpAlt = "fas fa-sort-numeric-up-alt fa-fw"; - public static readonly string SortUp = "fas fa-sort-up fa-fw"; - public static readonly string Spa = "fas fa-spa fa-fw"; - public static readonly string SpaceShuttle = "fas fa-space-shuttle fa-fw"; - public static readonly string SpellCheck = "fas fa-spell-check fa-fw"; - public static readonly string Spider = "fas fa-spider fa-fw"; - public static readonly string Spinner = "fas fa-spinner fa-fw"; - public static readonly string Splotch = "fas fa-splotch fa-fw"; - public static readonly string SprayCan = "fas fa-spray-can fa-fw"; - public static readonly string Square = "fas fa-square fa-fw"; - public static readonly string SquareFull = "fas fa-square-full fa-fw"; - public static readonly string SquareRootAlt = "fas fa-square-root-alt fa-fw"; - public static readonly string Stamp = "fas fa-stamp fa-fw"; - public static readonly string Star = "fas fa-star fa-fw"; - public static readonly string StarAndCrescent = "fas fa-star-and-crescent fa-fw"; - public static readonly string StarHalf = "fas fa-star-half fa-fw"; - public static readonly string StarHalfAlt = "fas fa-star-half-alt fa-fw"; - public static readonly string StarOfDavid = "fas fa-star-of-david fa-fw"; - public static readonly string StarOfLife = "fas fa-star-of-life fa-fw"; - public static readonly string StepBackward = "fas fa-step-backward fa-fw"; - public static readonly string StepForward = "fas fa-step-forward fa-fw"; - public static readonly string Stethoscope = "fas fa-stethoscope fa-fw"; - public static readonly string StickyNote = "fas fa-sticky-note fa-fw"; - public static readonly string Stop = "fas fa-stop fa-fw"; - public static readonly string StopCircle = "fas fa-stop-circle fa-fw"; - public static readonly string Stopwatch = "fas fa-stopwatch fa-fw"; - public static readonly string Store = "fas fa-store fa-fw"; - public static readonly string StoreAlt = "fas fa-store-alt fa-fw"; - public static readonly string Stream = "fas fa-stream fa-fw"; - public static readonly string StreetView = "fas fa-street-view fa-fw"; - public static readonly string Strikethrough = "fas fa-strikethrough fa-fw"; - public static readonly string Stroopwafel = "fas fa-stroopwafel fa-fw"; - public static readonly string Subscript = "fas fa-subscript fa-fw"; - public static readonly string Subway = "fas fa-subway fa-fw"; - public static readonly string Suitcase = "fas fa-suitcase fa-fw"; - public static readonly string SuitcaseRolling = "fas fa-suitcase-rolling fa-fw"; - public static readonly string Sun = "fas fa-sun fa-fw"; - public static readonly string Superscript = "fas fa-superscript fa-fw"; - public static readonly string Surprise = "fas fa-surprise fa-fw"; - public static readonly string Swatchbook = "fas fa-swatchbook fa-fw"; - public static readonly string Swimmer = "fas fa-swimmer fa-fw"; - public static readonly string SwimmingPool = "fas fa-swimming-pool fa-fw"; - public static readonly string Synagogue = "fas fa-synagogue fa-fw"; - public static readonly string Sync = "fas fa-sync fa-fw"; - public static readonly string SyncAlt = "fas fa-sync-alt fa-fw"; - public static readonly string Syringe = "fas fa-syringe fa-fw"; - public static readonly string Table = "fas fa-table fa-fw"; - public static readonly string TableTennis = "fas fa-table-tennis fa-fw"; - public static readonly string Tablet = "fas fa-tablet fa-fw"; - public static readonly string TabletAlt = "fas fa-tablet-alt fa-fw"; - public static readonly string Tablets = "fas fa-tablets fa-fw"; - public static readonly string TachometerAlt = "fas fa-tachometer-alt fa-fw"; - public static readonly string Tag = "fas fa-tag fa-fw"; - public static readonly string Tags = "fas fa-tags fa-fw"; - public static readonly string Tape = "fas fa-tape fa-fw"; - public static readonly string Tasks = "fas fa-tasks fa-fw"; - public static readonly string Taxi = "fas fa-taxi fa-fw"; - public static readonly string Teeth = "fas fa-teeth fa-fw"; - public static readonly string TeethOpen = "fas fa-teeth-open fa-fw"; - public static readonly string TemperatureHigh = "fas fa-temperature-high fa-fw"; - public static readonly string TemperatureLow = "fas fa-temperature-low fa-fw"; - public static readonly string Tenge = "fas fa-tenge fa-fw"; - public static readonly string Terminal = "fas fa-terminal fa-fw"; - public static readonly string TextHeight = "fas fa-text-height fa-fw"; - public static readonly string TextWidth = "fas fa-text-width fa-fw"; - public static readonly string Th = "fas fa-th fa-fw"; - public static readonly string ThLarge = "fas fa-th-large fa-fw"; - public static readonly string ThList = "fas fa-th-list fa-fw"; - public static readonly string TheaterMasks = "fas fa-theater-masks fa-fw"; - public static readonly string Thermometer = "fas fa-thermometer fa-fw"; - public static readonly string ThermometerEmpty = "fas fa-thermometer-empty fa-fw"; - public static readonly string ThermometerFull = "fas fa-thermometer-full fa-fw"; - public static readonly string ThermometerHalf = "fas fa-thermometer-half fa-fw"; - public static readonly string ThermometerQuarter = "fas fa-thermometer-quarter fa-fw"; - public static readonly string ThermometerThreeQuarters = "fas fa-thermometer-three-quarters fa-fw"; - public static readonly string ThumbsDown = "fas fa-thumbs-down fa-fw"; - public static readonly string ThumbsUp = "fas fa-thumbs-up fa-fw"; - public static readonly string Thumbtack = "fas fa-thumbtack fa-fw"; - public static readonly string TicketAlt = "fas fa-ticket-alt fa-fw"; - public static readonly string Times = "fas fa-times fa-fw"; - public static readonly string TimesCircle = "fas fa-times-circle fa-fw"; - public static readonly string Tint = "fas fa-tint fa-fw"; - public static readonly string TintSlash = "fas fa-tint-slash fa-fw"; - public static readonly string Tired = "fas fa-tired fa-fw"; - public static readonly string ToggleOff = "fas fa-toggle-off fa-fw"; - public static readonly string ToggleOn = "fas fa-toggle-on fa-fw"; - public static readonly string Toilet = "fas fa-toilet fa-fw"; - public static readonly string ToiletPaper = "fas fa-toilet-paper fa-fw"; - public static readonly string Toolbox = "fas fa-toolbox fa-fw"; - public static readonly string Tools = "fas fa-tools fa-fw"; - public static readonly string Tooth = "fas fa-tooth fa-fw"; - public static readonly string Torah = "fas fa-torah fa-fw"; - public static readonly string ToriiGate = "fas fa-torii-gate fa-fw"; - public static readonly string Tractor = "fas fa-tractor fa-fw"; - public static readonly string Trademark = "fas fa-trademark fa-fw"; - public static readonly string TrafficLight = "fas fa-traffic-light fa-fw"; - public static readonly string Train = "fas fa-train fa-fw"; - public static readonly string Tram = "fas fa-tram fa-fw"; - public static readonly string Transgender = "fas fa-transgender fa-fw"; - public static readonly string TransgenderAlt = "fas fa-transgender-alt fa-fw"; - public static readonly string Trash = "fas fa-trash fa-fw"; - public static readonly string TrashAlt = "fas fa-trash-alt fa-fw"; - public static readonly string TrashRestore = "fas fa-trash-restore fa-fw"; - public static readonly string TrashRestoreAlt = "fas fa-trash-restore-alt fa-fw"; - public static readonly string Tree = "fas fa-tree fa-fw"; - public static readonly string Trophy = "fas fa-trophy fa-fw"; - public static readonly string Truck = "fas fa-truck fa-fw"; - public static readonly string TruckLoading = "fas fa-truck-loading fa-fw"; - public static readonly string TruckMonster = "fas fa-truck-monster fa-fw"; - public static readonly string TruckMoving = "fas fa-truck-moving fa-fw"; - public static readonly string TruckPickup = "fas fa-truck-pickup fa-fw"; - public static readonly string Tshirt = "fas fa-tshirt fa-fw"; - public static readonly string Tty = "fas fa-tty fa-fw"; - public static readonly string Tv = "fas fa-tv fa-fw"; - public static readonly string Umbrella = "fas fa-umbrella fa-fw"; - public static readonly string UmbrellaBeach = "fas fa-umbrella-beach fa-fw"; - public static readonly string Underline = "fas fa-underline fa-fw"; - public static readonly string Undo = "fas fa-undo fa-fw"; - public static readonly string UndoAlt = "fas fa-undo-alt fa-fw"; - public static readonly string UniversalAccess = "fas fa-universal-access fa-fw"; - public static readonly string University = "fas fa-university fa-fw"; - public static readonly string Unlink = "fas fa-unlink fa-fw"; - public static readonly string Unlock = "fas fa-unlock fa-fw"; - public static readonly string UnlockAlt = "fas fa-unlock-alt fa-fw"; - public static readonly string Upload = "fas fa-upload fa-fw"; - public static readonly string User = "fas fa-user fa-fw"; - public static readonly string UserAlt = "fas fa-user-alt fa-fw"; - public static readonly string UserAltSlash = "fas fa-user-alt-slash fa-fw"; - public static readonly string UserAstronaut = "fas fa-user-astronaut fa-fw"; - public static readonly string UserCheck = "fas fa-user-check fa-fw"; - public static readonly string UserCircle = "fas fa-user-circle fa-fw"; - public static readonly string UserClock = "fas fa-user-clock fa-fw"; - public static readonly string UserCog = "fas fa-user-cog fa-fw"; - public static readonly string UserEdit = "fas fa-user-edit fa-fw"; - public static readonly string UserFriends = "fas fa-user-friends fa-fw"; - public static readonly string UserGraduate = "fas fa-user-graduate fa-fw"; - public static readonly string UserInjured = "fas fa-user-injured fa-fw"; - public static readonly string UserLock = "fas fa-user-lock fa-fw"; - public static readonly string UserMd = "fas fa-user-md fa-fw"; - public static readonly string UserMinus = "fas fa-user-minus fa-fw"; - public static readonly string UserNinja = "fas fa-user-ninja fa-fw"; - public static readonly string UserNurse = "fas fa-user-nurse fa-fw"; - public static readonly string UserPlus = "fas fa-user-plus fa-fw"; - public static readonly string UserSecret = "fas fa-user-secret fa-fw"; - public static readonly string UserShield = "fas fa-user-shield fa-fw"; - public static readonly string UserSlash = "fas fa-user-slash fa-fw"; - public static readonly string UserTag = "fas fa-user-tag fa-fw"; - public static readonly string UserTie = "fas fa-user-tie fa-fw"; - public static readonly string UserTimes = "fas fa-user-times fa-fw"; - public static readonly string Users = "fas fa-users fa-fw"; - public static readonly string UsersCog = "fas fa-users-cog fa-fw"; - public static readonly string UtensilSpoon = "fas fa-utensil-spoon fa-fw"; - public static readonly string Utensils = "fas fa-utensils fa-fw"; - public static readonly string VectorSquare = "fas fa-vector-square fa-fw"; - public static readonly string Venus = "fas fa-venus fa-fw"; - public static readonly string VenusDouble = "fas fa-venus-double fa-fw"; - public static readonly string VenusMars = "fas fa-venus-mars fa-fw"; - public static readonly string Vial = "fas fa-vial fa-fw"; - public static readonly string Vials = "fas fa-vials fa-fw"; - public static readonly string Video = "fas fa-video fa-fw"; - public static readonly string VideoSlash = "fas fa-video-slash fa-fw"; - public static readonly string Vihara = "fas fa-vihara fa-fw"; - public static readonly string Voicemail = "fas fa-voicemail fa-fw"; - public static readonly string VolleyballBall = "fas fa-volleyball-ball fa-fw"; - public static readonly string VolumeDown = "fas fa-volume-down fa-fw"; - public static readonly string VolumeMute = "fas fa-volume-mute fa-fw"; - public static readonly string VolumeOff = "fas fa-volume-off fa-fw"; - public static readonly string VolumeUp = "fas fa-volume-up fa-fw"; - public static readonly string VoteYea = "fas fa-vote-yea fa-fw"; - public static readonly string VrCardboard = "fas fa-vr-cardboard fa-fw"; - public static readonly string Walking = "fas fa-walking fa-fw"; - public static readonly string Wallet = "fas fa-wallet fa-fw"; - public static readonly string Warehouse = "fas fa-warehouse fa-fw"; - public static readonly string Water = "fas fa-water fa-fw"; - public static readonly string WaveSquare = "fas fa-wave-square fa-fw"; - public static readonly string Weight = "fas fa-weight fa-fw"; - public static readonly string WeightHanging = "fas fa-weight-hanging fa-fw"; - public static readonly string Wheelchair = "fas fa-wheelchair fa-fw"; - public static readonly string Wifi = "fas fa-wifi fa-fw"; - public static readonly string Wind = "fas fa-wind fa-fw"; - public static readonly string WindowClose = "fas fa-window-close fa-fw"; - public static readonly string WindowMaximize = "fas fa-window-maximize fa-fw"; - public static readonly string WindowMinimize = "fas fa-window-minimize fa-fw"; - public static readonly string WindowRestore = "fas fa-window-restore fa-fw"; - public static readonly string WineBottle = "fas fa-wine-bottle fa-fw"; - public static readonly string WineGlass = "fas fa-wine-glass fa-fw"; - public static readonly string WineGlassAlt = "fas fa-wine-glass-alt fa-fw"; - public static readonly string WonSign = "fas fa-won-sign fa-fw"; - public static readonly string Wrench = "fas fa-wrench fa-fw"; - public static readonly string XRay = "fas fa-x-ray fa-fw"; - public static readonly string YenSign = "fas fa-yen-sign fa-fw"; - public static readonly string YinYang = "fas fa-yin-yang fa-fw"; - - //Regular Icons - public static readonly string AddressBookRegular = "far fa-address-book fa-fw"; - public static readonly string AddressCardRegular = "far fa-address-card fa-fw"; - public static readonly string AngryRegular = "far fa-angry fa-fw"; - public static readonly string ArrowAltCircleDownRegular = "far fa-arrow-alt-circle-down fa-fw"; - public static readonly string ArrowAltCircleLeftRegular = "far fa-arrow-alt-circle-left fa-fw"; - public static readonly string ArrowAltCircleRightRegular = "far fa-arrow-alt-circle-right fa-fw"; - public static readonly string ArrowAltCircleUpRegular = "far fa-arrow-alt-circle-up fa-fw"; - public static readonly string BellRegular = "far fa-bell fa-fw"; - public static readonly string BellSlashRegular = "far fa-bell-slash fa-fw"; - public static readonly string BookmarkRegular = "far fa-bookmark fa-fw"; - public static readonly string BuildingRegular = "far fa-building fa-fw"; - public static readonly string CalendarRegular = "far fa-calendar fa-fw"; - public static readonly string CalendarAltRegular = "far fa-calendar-alt fa-fw"; - public static readonly string CalendarCheckRegular = "far fa-calendar-check fa-fw"; - public static readonly string CalendarMinusRegular = "far fa-calendar-minus fa-fw"; - public static readonly string CalendarPlusRegular = "far fa-calendar-plus fa-fw"; - public static readonly string CalendarTimesRegular = "far fa-calendar-times fa-fw"; - public static readonly string CaretSquareDownRegular = "far fa-caret-square-down fa-fw"; - public static readonly string CaretSquareLeftRegular = "far fa-caret-square-left fa-fw"; - public static readonly string CaretSquareRightRegular = "far fa-caret-square-right fa-fw"; - public static readonly string CaretSquareUpRegular = "far fa-caret-square-up fa-fw"; - public static readonly string ChartBarRegular = "far fa-chart-bar fa-fw"; - public static readonly string CheckCircleRegular = "far fa-check-circle fa-fw"; - public static readonly string CheckSquareRegular = "far fa-check-square fa-fw"; - public static readonly string CircleRegular = "far fa-circle fa-fw"; - public static readonly string ClipboardRegular = "far fa-clipboard fa-fw"; - public static readonly string ClockRegular = "far fa-clock fa-fw"; - public static readonly string CloneRegular = "far fa-clone fa-fw"; - public static readonly string ClosedCaptioningRegular = "far fa-closed-captioning fa-fw"; - public static readonly string CommentRegular = "far fa-comment fa-fw"; - public static readonly string CommentAltRegular = "far fa-comment-alt fa-fw"; - public static readonly string CommentDotsRegular = "far fa-comment-dots fa-fw"; - public static readonly string CommentsRegular = "far fa-comments fa-fw"; - public static readonly string CompassRegular = "far fa-compass fa-fw"; - public static readonly string CopyRegular = "far fa-copy fa-fw"; - public static readonly string CopyrightRegular = "far fa-copyright fa-fw"; - public static readonly string CreditCardRegular = "far fa-credit-card fa-fw"; - public static readonly string DizzyRegular = "far fa-dizzy fa-fw"; - public static readonly string DotCircleRegular = "far fa-dot-circle fa-fw"; - public static readonly string EditRegular = "far fa-edit fa-fw"; - public static readonly string EnvelopeRegular = "far fa-envelope fa-fw"; - public static readonly string EnvelopeOpenRegular = "far fa-envelope-open fa-fw"; - public static readonly string EyeRegular = "far fa-eye fa-fw"; - public static readonly string EyeSlashRegular = "far fa-eye-slash fa-fw"; - public static readonly string FileRegular = "far fa-file fa-fw"; - public static readonly string FileAltRegular = "far fa-file-alt fa-fw"; - public static readonly string FileArchiveRegular = "far fa-file-archive fa-fw"; - public static readonly string FileAudioRegular = "far fa-file-audio fa-fw"; - public static readonly string FileCodeRegular = "far fa-file-code fa-fw"; - public static readonly string FileExcelRegular = "far fa-file-excel fa-fw"; - public static readonly string FileImageRegular = "far fa-file-image fa-fw"; - public static readonly string FilePdfRegular = "far fa-file-pdf fa-fw"; - public static readonly string FilePowerpointRegular = "far fa-file-powerpoint fa-fw"; - public static readonly string FileVideoRegular = "far fa-file-video fa-fw"; - public static readonly string FileWordRegular = "far fa-file-word fa-fw"; - public static readonly string FlagRegular = "far fa-flag fa-fw"; - public static readonly string FlushedRegular = "far fa-flushed fa-fw"; - public static readonly string FolderRegular = "far fa-folder fa-fw"; - public static readonly string FolderOpenRegular = "far fa-folder-open fa-fw"; - public static readonly string FrownRegular = "far fa-frown fa-fw"; - public static readonly string FrownOpenRegular = "far fa-frown-open fa-fw"; - public static readonly string FutbolRegular = "far fa-futbol fa-fw"; - public static readonly string GemRegular = "far fa-gem fa-fw"; - public static readonly string GrimaceRegular = "far fa-grimace fa-fw"; - public static readonly string GrinRegular = "far fa-grin fa-fw"; - public static readonly string GrinAltRegular = "far fa-grin-alt fa-fw"; - public static readonly string GrinBeamRegular = "far fa-grin-beam fa-fw"; - public static readonly string GrinBeamSweatRegular = "far fa-grin-beam-sweat fa-fw"; - public static readonly string GrinHeartsRegular = "far fa-grin-hearts fa-fw"; - public static readonly string GrinSquintRegular = "far fa-grin-squint fa-fw"; - public static readonly string GrinSquintTearsRegular = "far fa-grin-squint-tears fa-fw"; - public static readonly string GrinStarsRegular = "far fa-grin-stars fa-fw"; - public static readonly string GrinTearsRegular = "far fa-grin-tears fa-fw"; - public static readonly string GrinTongueRegular = "far fa-grin-tongue fa-fw"; - public static readonly string GrinTongueSquintRegular = "far fa-grin-tongue-squint fa-fw"; - public static readonly string GrinTongueWinkRegular = "far fa-grin-tongue-wink fa-fw"; - public static readonly string GrinWinkRegular = "far fa-grin-wink fa-fw"; - public static readonly string HandLizardRegular = "far fa-hand-lizard fa-fw"; - public static readonly string HandPaperRegular = "far fa-hand-paper fa-fw"; - public static readonly string HandPeaceRegular = "far fa-hand-peace fa-fw"; - public static readonly string HandPointDownRegular = "far fa-hand-point-down fa-fw"; - public static readonly string HandPointLeftRegular = "far fa-hand-point-left fa-fw"; - public static readonly string HandPointRightRegular = "far fa-hand-point-right fa-fw"; - public static readonly string HandPointUpRegular = "far fa-hand-point-up fa-fw"; - public static readonly string HandPointerRegular = "far fa-hand-pointer fa-fw"; - public static readonly string HandRockRegular = "far fa-hand-rock fa-fw"; - public static readonly string HandScissorsRegular = "far fa-hand-scissors fa-fw"; - public static readonly string HandSpockRegular = "far fa-hand-spock fa-fw"; - public static readonly string HandshakeRegular = "far fa-handshake fa-fw"; - public static readonly string HddRegular = "far fa-hdd fa-fw"; - public static readonly string HeartRegular = "far fa-heart fa-fw"; - public static readonly string HospitalRegular = "far fa-hospital fa-fw"; - public static readonly string HourglassRegular = "far fa-hourglass fa-fw"; - public static readonly string IdBadgeRegular = "far fa-id-badge fa-fw"; - public static readonly string IdCardRegular = "far fa-id-card fa-fw"; - public static readonly string ImageRegular = "far fa-image fa-fw"; - public static readonly string ImagesRegular = "far fa-images fa-fw"; - public static readonly string KeyboardRegular = "far fa-keyboard fa-fw"; - public static readonly string KissRegular = "far fa-kiss fa-fw"; - public static readonly string KissBeamRegular = "far fa-kiss-beam fa-fw"; - public static readonly string KissWinkHeartRegular = "far fa-kiss-wink-heart fa-fw"; - public static readonly string LaughRegular = "far fa-laugh fa-fw"; - public static readonly string LaughBeamRegular = "far fa-laugh-beam fa-fw"; - public static readonly string LaughSquintRegular = "far fa-laugh-squint fa-fw"; - public static readonly string LaughWinkRegular = "far fa-laugh-wink fa-fw"; - public static readonly string LemonRegular = "far fa-lemon fa-fw"; - public static readonly string LifeRingRegular = "far fa-life-ring fa-fw"; - public static readonly string LightbulbRegular = "far fa-lightbulb fa-fw"; - public static readonly string ListAltRegular = "far fa-list-alt fa-fw"; - public static readonly string MapRegular = "far fa-map fa-fw"; - public static readonly string MehRegular = "far fa-meh fa-fw"; - public static readonly string MehBlankRegular = "far fa-meh-blank fa-fw"; - public static readonly string MehRollingEyesRegular = "far fa-meh-rolling-eyes fa-fw"; - public static readonly string MinusSquareRegular = "far fa-minus-square fa-fw"; - public static readonly string MoneyBillAltRegular = "far fa-money-bill-alt fa-fw"; - public static readonly string MoonRegular = "far fa-moon fa-fw"; - public static readonly string NewspaperRegular = "far fa-newspaper fa-fw"; - public static readonly string ObjectGroupRegular = "far fa-object-group fa-fw"; - public static readonly string ObjectUngroupRegular = "far fa-object-ungroup fa-fw"; - public static readonly string PaperPlaneRegular = "far fa-paper-plane fa-fw"; - public static readonly string PauseCircleRegular = "far fa-pause-circle fa-fw"; - public static readonly string PlayCircleRegular = "far fa-play-circle fa-fw"; - public static readonly string PlusSquareRegular = "far fa-plus-square fa-fw"; - public static readonly string QuestionCircleRegular = "far fa-question-circle fa-fw"; - public static readonly string RegisteredRegular = "far fa-registered fa-fw"; - public static readonly string SadCryRegular = "far fa-sad-cry fa-fw"; - public static readonly string SadTearRegular = "far fa-sad-tear fa-fw"; - public static readonly string SaveRegular = "far fa-save fa-fw"; - public static readonly string ShareSquareRegular = "far fa-share-square fa-fw"; - public static readonly string SmileRegular = "far fa-smile fa-fw"; - public static readonly string SmileBeamRegular = "far fa-smile-beam fa-fw"; - public static readonly string SmileWinkRegular = "far fa-smile-wink fa-fw"; - public static readonly string SnowflakeRegular = "far fa-snowflake fa-fw"; - public static readonly string SquareRegular = "far fa-square fa-fw"; - public static readonly string StarRegular = "far fa-star fa-fw"; - public static readonly string StarHalfRegular = "far fa-star-half fa-fw"; - public static readonly string StickyNoteRegular = "far fa-sticky-note fa-fw"; - public static readonly string StopCircleRegular = "far fa-stop-circle fa-fw"; - public static readonly string SunRegular = "far fa-sun fa-fw"; - public static readonly string SurpriseRegular = "far fa-surprise fa-fw"; - public static readonly string ThumbsDownRegular = "far fa-thumbs-down fa-fw"; - public static readonly string ThumbsUpRegular = "far fa-thumbs-up fa-fw"; - public static readonly string TimesCircleRegular = "far fa-times-circle fa-fw"; - public static readonly string TiredRegular = "far fa-tired fa-fw"; - public static readonly string TrashAltRegular = "far fa-trash-alt fa-fw"; - public static readonly string UserRegular = "far fa-user fa-fw"; - public static readonly string UserCircleRegular = "far fa-user-circle fa-fw"; - public static readonly string WindowCloseRegular = "far fa-window-close fa-fw"; - public static readonly string WindowMaximizeRegular = "far fa-window-maximize fa-fw"; - public static readonly string WindowMinimizeRegular = "far fa-window-minimize fa-fw"; - public static readonly string WindowRestoreRegular = "far fa-window-restore fa-fw"; - - //Brand Icons - public static readonly string _500Px = "fab fa-500px fa-fw"; - public static readonly string AccessibleIcon = "fab fa-accessible-icon fa-fw"; - public static readonly string Accusoft = "fab fa-accusoft fa-fw"; - public static readonly string AcquisitionsIncorporated = "fab fa-acquisitions-incorporated fa-fw"; - public static readonly string Adn = "fab fa-adn fa-fw"; - public static readonly string Adobe = "fab fa-adobe fa-fw"; - public static readonly string Adversal = "fab fa-adversal fa-fw"; - public static readonly string Affiliatetheme = "fab fa-affiliatetheme fa-fw"; - public static readonly string Airbnb = "fab fa-airbnb fa-fw"; - public static readonly string Algolia = "fab fa-algolia fa-fw"; - public static readonly string Alipay = "fab fa-alipay fa-fw"; - public static readonly string Amazon = "fab fa-amazon fa-fw"; - public static readonly string AmazonPay = "fab fa-amazon-pay fa-fw"; - public static readonly string Amilia = "fab fa-amilia fa-fw"; - public static readonly string Android = "fab fa-android fa-fw"; - public static readonly string Angellist = "fab fa-angellist fa-fw"; - public static readonly string Angrycreative = "fab fa-angrycreative fa-fw"; - public static readonly string Angular = "fab fa-angular fa-fw"; - public static readonly string AppStore = "fab fa-app-store fa-fw"; - public static readonly string AppStoreIos = "fab fa-app-store-ios fa-fw"; - public static readonly string Apper = "fab fa-apper fa-fw"; - public static readonly string Apple = "fab fa-apple fa-fw"; - public static readonly string ApplePay = "fab fa-apple-pay fa-fw"; - public static readonly string Artstation = "fab fa-artstation fa-fw"; - public static readonly string Asymmetrik = "fab fa-asymmetrik fa-fw"; - public static readonly string Atlassian = "fab fa-atlassian fa-fw"; - public static readonly string Audible = "fab fa-audible fa-fw"; - public static readonly string Autoprefixer = "fab fa-autoprefixer fa-fw"; - public static readonly string Avianex = "fab fa-avianex fa-fw"; - public static readonly string Aviato = "fab fa-aviato fa-fw"; - public static readonly string Aws = "fab fa-aws fa-fw"; - public static readonly string Bandcamp = "fab fa-bandcamp fa-fw"; - public static readonly string BattleNet = "fab fa-battle-net fa-fw"; - public static readonly string Behance = "fab fa-behance fa-fw"; - public static readonly string BehanceSquare = "fab fa-behance-square fa-fw"; - public static readonly string Bimobject = "fab fa-bimobject fa-fw"; - public static readonly string Bitbucket = "fab fa-bitbucket fa-fw"; - public static readonly string Bitcoin = "fab fa-bitcoin fa-fw"; - public static readonly string Bity = "fab fa-bity fa-fw"; - public static readonly string BlackTie = "fab fa-black-tie fa-fw"; - public static readonly string Blackberry = "fab fa-blackberry fa-fw"; - public static readonly string Blogger = "fab fa-blogger fa-fw"; - public static readonly string BloggerB = "fab fa-blogger-b fa-fw"; - public static readonly string Bluetooth = "fab fa-bluetooth fa-fw"; - public static readonly string BluetoothB = "fab fa-bluetooth-b fa-fw"; - public static readonly string Bootstrap = "fab fa-bootstrap fa-fw"; - public static readonly string Btc = "fab fa-btc fa-fw"; - public static readonly string Buffer = "fab fa-buffer fa-fw"; - public static readonly string Buromobelexperte = "fab fa-buromobelexperte fa-fw"; - public static readonly string BuyNLarge = "fab fa-buy-n-large fa-fw"; - public static readonly string CanadianMapleLeaf = "fab fa-canadian-maple-leaf fa-fw"; - public static readonly string CcAmazonPay = "fab fa-cc-amazon-pay fa-fw"; - public static readonly string CcAmex = "fab fa-cc-amex fa-fw"; - public static readonly string CcApplePay = "fab fa-cc-apple-pay fa-fw"; - public static readonly string CcDinersClub = "fab fa-cc-diners-club fa-fw"; - public static readonly string CcDiscover = "fab fa-cc-discover fa-fw"; - public static readonly string CcJcb = "fab fa-cc-jcb fa-fw"; - public static readonly string CcMastercard = "fab fa-cc-mastercard fa-fw"; - public static readonly string CcPaypal = "fab fa-cc-paypal fa-fw"; - public static readonly string CcStripe = "fab fa-cc-stripe fa-fw"; - public static readonly string CcVisa = "fab fa-cc-visa fa-fw"; - public static readonly string Centercode = "fab fa-centercode fa-fw"; - public static readonly string Centos = "fab fa-centos fa-fw"; - public static readonly string Chrome = "fab fa-chrome fa-fw"; - public static readonly string Chromecast = "fab fa-chromecast fa-fw"; - public static readonly string Cloudscale = "fab fa-cloudscale fa-fw"; - public static readonly string Cloudsmith = "fab fa-cloudsmith fa-fw"; - public static readonly string Cloudversify = "fab fa-cloudversify fa-fw"; - public static readonly string Codepen = "fab fa-codepen fa-fw"; - public static readonly string Codiepie = "fab fa-codiepie fa-fw"; - public static readonly string Confluence = "fab fa-confluence fa-fw"; - public static readonly string Connectdevelop = "fab fa-connectdevelop fa-fw"; - public static readonly string Contao = "fab fa-contao fa-fw"; - public static readonly string CottonBureau = "fab fa-cotton-bureau fa-fw"; - public static readonly string Cpanel = "fab fa-cpanel fa-fw"; - public static readonly string CreativeCommons = "fab fa-creative-commons fa-fw"; - public static readonly string CreativeCommonsBy = "fab fa-creative-commons-by fa-fw"; - public static readonly string CreativeCommonsNc = "fab fa-creative-commons-nc fa-fw"; - public static readonly string CreativeCommonsNcEu = "fab fa-creative-commons-nc-eu fa-fw"; - public static readonly string CreativeCommonsNcJp = "fab fa-creative-commons-nc-jp fa-fw"; - public static readonly string CreativeCommonsNd = "fab fa-creative-commons-nd fa-fw"; - public static readonly string CreativeCommonsPd = "fab fa-creative-commons-pd fa-fw"; - public static readonly string CreativeCommonsPdAlt = "fab fa-creative-commons-pd-alt fa-fw"; - public static readonly string CreativeCommonsRemix = "fab fa-creative-commons-remix fa-fw"; - public static readonly string CreativeCommonsSa = "fab fa-creative-commons-sa fa-fw"; - public static readonly string CreativeCommonsSampling = "fab fa-creative-commons-sampling fa-fw"; - public static readonly string CreativeCommonsSamplingPlus = "fab fa-creative-commons-sampling-plus fa-fw"; - public static readonly string CreativeCommonsShare = "fab fa-creative-commons-share fa-fw"; - public static readonly string CreativeCommonsZero = "fab fa-creative-commons-zero fa-fw"; - public static readonly string CriticalRole = "fab fa-critical-role fa-fw"; - public static readonly string Css3 = "fab fa-css3 fa-fw"; - public static readonly string Css3Alt = "fab fa-css3-alt fa-fw"; - public static readonly string Cuttlefish = "fab fa-cuttlefish fa-fw"; - public static readonly string DAndD = "fab fa-d-and-d fa-fw"; - public static readonly string DAndDBeyond = "fab fa-d-and-d-beyond fa-fw"; - public static readonly string Dashcube = "fab fa-dashcube fa-fw"; - public static readonly string Delicious = "fab fa-delicious fa-fw"; - public static readonly string Deploydog = "fab fa-deploydog fa-fw"; - public static readonly string Deskpro = "fab fa-deskpro fa-fw"; - public static readonly string Dev = "fab fa-dev fa-fw"; - public static readonly string Deviantart = "fab fa-deviantart fa-fw"; - public static readonly string Dhl = "fab fa-dhl fa-fw"; - public static readonly string Diaspora = "fab fa-diaspora fa-fw"; - public static readonly string Digg = "fab fa-digg fa-fw"; - public static readonly string DigitalOcean = "fab fa-digital-ocean fa-fw"; - public static readonly string Discord = "fab fa-discord fa-fw"; - public static readonly string Discourse = "fab fa-discourse fa-fw"; - public static readonly string Dochub = "fab fa-dochub fa-fw"; - public static readonly string Docker = "fab fa-docker fa-fw"; - public static readonly string Draft2Digital = "fab fa-draft2digital fa-fw"; - public static readonly string Dribbble = "fab fa-dribbble fa-fw"; - public static readonly string DribbbleSquare = "fab fa-dribbble-square fa-fw"; - public static readonly string Dropbox = "fab fa-dropbox fa-fw"; - public static readonly string Drupal = "fab fa-drupal fa-fw"; - public static readonly string Dyalog = "fab fa-dyalog fa-fw"; - public static readonly string Earlybirds = "fab fa-earlybirds fa-fw"; - public static readonly string Ebay = "fab fa-ebay fa-fw"; - public static readonly string Edge = "fab fa-edge fa-fw"; - public static readonly string Elementor = "fab fa-elementor fa-fw"; - public static readonly string Ello = "fab fa-ello fa-fw"; - public static readonly string Ember = "fab fa-ember fa-fw"; - public static readonly string Empire = "fab fa-empire fa-fw"; - public static readonly string Envira = "fab fa-envira fa-fw"; - public static readonly string Erlang = "fab fa-erlang fa-fw"; - public static readonly string Ethereum = "fab fa-ethereum fa-fw"; - public static readonly string Etsy = "fab fa-etsy fa-fw"; - public static readonly string Evernote = "fab fa-evernote fa-fw"; - public static readonly string Expeditedssl = "fab fa-expeditedssl fa-fw"; - public static readonly string Facebook = "fab fa-facebook fa-fw"; - public static readonly string FacebookF = "fab fa-facebook-f fa-fw"; - public static readonly string FacebookMessenger = "fab fa-facebook-messenger fa-fw"; - public static readonly string FacebookSquare = "fab fa-facebook-square fa-fw"; - public static readonly string FantasyFlightGames = "fab fa-fantasy-flight-games fa-fw"; - public static readonly string Fedex = "fab fa-fedex fa-fw"; - public static readonly string Fedora = "fab fa-fedora fa-fw"; - public static readonly string Figma = "fab fa-figma fa-fw"; - public static readonly string Firefox = "fab fa-firefox fa-fw"; - public static readonly string FirstOrder = "fab fa-first-order fa-fw"; - public static readonly string FirstOrderAlt = "fab fa-first-order-alt fa-fw"; - public static readonly string Firstdraft = "fab fa-firstdraft fa-fw"; - public static readonly string Flickr = "fab fa-flickr fa-fw"; - public static readonly string Flipboard = "fab fa-flipboard fa-fw"; - public static readonly string Fly = "fab fa-fly fa-fw"; - public static readonly string FontAwesome = "fab fa-font-awesome fa-fw"; - public static readonly string FontAwesomeAlt = "fab fa-font-awesome-alt fa-fw"; - public static readonly string FontAwesomeFlag = "fab fa-font-awesome-flag fa-fw"; - public static readonly string Fonticons = "fab fa-fonticons fa-fw"; - public static readonly string FonticonsFi = "fab fa-fonticons-fi fa-fw"; - public static readonly string FortAwesome = "fab fa-fort-awesome fa-fw"; - public static readonly string FortAwesomeAlt = "fab fa-fort-awesome-alt fa-fw"; - public static readonly string Forumbee = "fab fa-forumbee fa-fw"; - public static readonly string Foursquare = "fab fa-foursquare fa-fw"; - public static readonly string FreeCodeCamp = "fab fa-free-code-camp fa-fw"; - public static readonly string Freebsd = "fab fa-freebsd fa-fw"; - public static readonly string Fulcrum = "fab fa-fulcrum fa-fw"; - public static readonly string GalacticRepublic = "fab fa-galactic-republic fa-fw"; - public static readonly string GalacticSenate = "fab fa-galactic-senate fa-fw"; - public static readonly string GetPocket = "fab fa-get-pocket fa-fw"; - public static readonly string Gg = "fab fa-gg fa-fw"; - public static readonly string GgCircle = "fab fa-gg-circle fa-fw"; - public static readonly string Git = "fab fa-git fa-fw"; - public static readonly string GitAlt = "fab fa-git-alt fa-fw"; - public static readonly string GitSquare = "fab fa-git-square fa-fw"; - public static readonly string Github = "fab fa-github fa-fw"; - public static readonly string GithubAlt = "fab fa-github-alt fa-fw"; - public static readonly string GithubSquare = "fab fa-github-square fa-fw"; - public static readonly string Gitkraken = "fab fa-gitkraken fa-fw"; - public static readonly string Gitlab = "fab fa-gitlab fa-fw"; - public static readonly string Gitter = "fab fa-gitter fa-fw"; - public static readonly string Glide = "fab fa-glide fa-fw"; - public static readonly string GlideG = "fab fa-glide-g fa-fw"; - public static readonly string Gofore = "fab fa-gofore fa-fw"; - public static readonly string Goodreads = "fab fa-goodreads fa-fw"; - public static readonly string GoodreadsG = "fab fa-goodreads-g fa-fw"; - public static readonly string Google = "fab fa-google fa-fw"; - public static readonly string GoogleDrive = "fab fa-google-drive fa-fw"; - public static readonly string GooglePlay = "fab fa-google-play fa-fw"; - public static readonly string GooglePlus = "fab fa-google-plus fa-fw"; - public static readonly string GooglePlusG = "fab fa-google-plus-g fa-fw"; - public static readonly string GooglePlusSquare = "fab fa-google-plus-square fa-fw"; - public static readonly string GoogleWallet = "fab fa-google-wallet fa-fw"; - public static readonly string Gratipay = "fab fa-gratipay fa-fw"; - public static readonly string Grav = "fab fa-grav fa-fw"; - public static readonly string Gripfire = "fab fa-gripfire fa-fw"; - public static readonly string Grunt = "fab fa-grunt fa-fw"; - public static readonly string Gulp = "fab fa-gulp fa-fw"; - public static readonly string HackerNews = "fab fa-hacker-news fa-fw"; - public static readonly string HackerNewsSquare = "fab fa-hacker-news-square fa-fw"; - public static readonly string Hackerrank = "fab fa-hackerrank fa-fw"; - public static readonly string Hips = "fab fa-hips fa-fw"; - public static readonly string HireAHelper = "fab fa-hire-a-helper fa-fw"; - public static readonly string Hooli = "fab fa-hooli fa-fw"; - public static readonly string Hornbill = "fab fa-hornbill fa-fw"; - public static readonly string Hotjar = "fab fa-hotjar fa-fw"; - public static readonly string Houzz = "fab fa-houzz fa-fw"; - public static readonly string Html5 = "fab fa-html5 fa-fw"; - public static readonly string Hubspot = "fab fa-hubspot fa-fw"; - public static readonly string Imdb = "fab fa-imdb fa-fw"; - public static readonly string Instagram = "fab fa-instagram fa-fw"; - public static readonly string Intercom = "fab fa-intercom fa-fw"; - public static readonly string InternetExplorer = "fab fa-internet-explorer fa-fw"; - public static readonly string Invision = "fab fa-invision fa-fw"; - public static readonly string Ioxhost = "fab fa-ioxhost fa-fw"; - public static readonly string ItchIo = "fab fa-itch-io fa-fw"; - public static readonly string Itunes = "fab fa-itunes fa-fw"; - public static readonly string ItunesNote = "fab fa-itunes-note fa-fw"; - public static readonly string Java = "fab fa-java fa-fw"; - public static readonly string JediOrder = "fab fa-jedi-order fa-fw"; - public static readonly string Jenkins = "fab fa-jenkins fa-fw"; - public static readonly string Jira = "fab fa-jira fa-fw"; - public static readonly string Joget = "fab fa-joget fa-fw"; - public static readonly string Joomla = "fab fa-joomla fa-fw"; - public static readonly string Js = "fab fa-js fa-fw"; - public static readonly string JsSquare = "fab fa-js-square fa-fw"; - public static readonly string Jsfiddle = "fab fa-jsfiddle fa-fw"; - public static readonly string Kaggle = "fab fa-kaggle fa-fw"; - public static readonly string Keybase = "fab fa-keybase fa-fw"; - public static readonly string Keycdn = "fab fa-keycdn fa-fw"; - public static readonly string Kickstarter = "fab fa-kickstarter fa-fw"; - public static readonly string KickstarterK = "fab fa-kickstarter-k fa-fw"; - public static readonly string Korvue = "fab fa-korvue fa-fw"; - public static readonly string Laravel = "fab fa-laravel fa-fw"; - public static readonly string Lastfm = "fab fa-lastfm fa-fw"; - public static readonly string LastfmSquare = "fab fa-lastfm-square fa-fw"; - public static readonly string Leanpub = "fab fa-leanpub fa-fw"; - public static readonly string Less = "fab fa-less fa-fw"; - public static readonly string Line = "fab fa-line fa-fw"; - public static readonly string Linkedin = "fab fa-linkedin fa-fw"; - public static readonly string LinkedinIn = "fab fa-linkedin-in fa-fw"; - public static readonly string Linode = "fab fa-linode fa-fw"; - public static readonly string Linux = "fab fa-linux fa-fw"; - public static readonly string Lyft = "fab fa-lyft fa-fw"; - public static readonly string Magento = "fab fa-magento fa-fw"; - public static readonly string Mailchimp = "fab fa-mailchimp fa-fw"; - public static readonly string Mandalorian = "fab fa-mandalorian fa-fw"; - public static readonly string Markdown = "fab fa-markdown fa-fw"; - public static readonly string Mastodon = "fab fa-mastodon fa-fw"; - public static readonly string Maxcdn = "fab fa-maxcdn fa-fw"; - public static readonly string Mdb = "fab fa-mdb fa-fw"; - public static readonly string Medapps = "fab fa-medapps fa-fw"; - public static readonly string Medium = "fab fa-medium fa-fw"; - public static readonly string MediumM = "fab fa-medium-m fa-fw"; - public static readonly string Medrt = "fab fa-medrt fa-fw"; - public static readonly string Meetup = "fab fa-meetup fa-fw"; - public static readonly string Megaport = "fab fa-megaport fa-fw"; - public static readonly string Mendeley = "fab fa-mendeley fa-fw"; - public static readonly string Microsoft = "fab fa-microsoft fa-fw"; - public static readonly string Mix = "fab fa-mix fa-fw"; - public static readonly string Mixcloud = "fab fa-mixcloud fa-fw"; - public static readonly string Mizuni = "fab fa-mizuni fa-fw"; - public static readonly string Modx = "fab fa-modx fa-fw"; - public static readonly string Monero = "fab fa-monero fa-fw"; - public static readonly string Napster = "fab fa-napster fa-fw"; - public static readonly string Neos = "fab fa-neos fa-fw"; - public static readonly string Nimblr = "fab fa-nimblr fa-fw"; - public static readonly string Node = "fab fa-node fa-fw"; - public static readonly string NodeJs = "fab fa-node-js fa-fw"; - public static readonly string Npm = "fab fa-npm fa-fw"; - public static readonly string Ns8 = "fab fa-ns8 fa-fw"; - public static readonly string Nutritionix = "fab fa-nutritionix fa-fw"; - public static readonly string Odnoklassniki = "fab fa-odnoklassniki fa-fw"; - public static readonly string OdnoklassnikiSquare = "fab fa-odnoklassniki-square fa-fw"; - public static readonly string OldRepublic = "fab fa-old-republic fa-fw"; - public static readonly string Opencart = "fab fa-opencart fa-fw"; - public static readonly string Openid = "fab fa-openid fa-fw"; - public static readonly string Opera = "fab fa-opera fa-fw"; - public static readonly string OptinMonster = "fab fa-optin-monster fa-fw"; - public static readonly string Orcid = "fab fa-orcid fa-fw"; - public static readonly string Osi = "fab fa-osi fa-fw"; - public static readonly string Page4 = "fab fa-page4 fa-fw"; - public static readonly string Pagelines = "fab fa-pagelines fa-fw"; - public static readonly string Palfed = "fab fa-palfed fa-fw"; - public static readonly string Patreon = "fab fa-patreon fa-fw"; - public static readonly string Paypal = "fab fa-paypal fa-fw"; - public static readonly string PennyArcade = "fab fa-penny-arcade fa-fw"; - public static readonly string Periscope = "fab fa-periscope fa-fw"; - public static readonly string Phabricator = "fab fa-phabricator fa-fw"; - public static readonly string PhoenixFramework = "fab fa-phoenix-framework fa-fw"; - public static readonly string PhoenixSquadron = "fab fa-phoenix-squadron fa-fw"; - public static readonly string Php = "fab fa-php fa-fw"; - public static readonly string PiedPiper = "fab fa-pied-piper fa-fw"; - public static readonly string PiedPiperAlt = "fab fa-pied-piper-alt fa-fw"; - public static readonly string PiedPiperHat = "fab fa-pied-piper-hat fa-fw"; - public static readonly string PiedPiperPp = "fab fa-pied-piper-pp fa-fw"; - public static readonly string Pinterest = "fab fa-pinterest fa-fw"; - public static readonly string PinterestP = "fab fa-pinterest-p fa-fw"; - public static readonly string PinterestSquare = "fab fa-pinterest-square fa-fw"; - public static readonly string Playstation = "fab fa-playstation fa-fw"; - public static readonly string ProductHunt = "fab fa-product-hunt fa-fw"; - public static readonly string Pushed = "fab fa-pushed fa-fw"; - public static readonly string Python = "fab fa-python fa-fw"; - public static readonly string Qq = "fab fa-qq fa-fw"; - public static readonly string Quinscape = "fab fa-quinscape fa-fw"; - public static readonly string Quora = "fab fa-quora fa-fw"; - public static readonly string RProject = "fab fa-r-project fa-fw"; - public static readonly string RaspberryPi = "fab fa-raspberry-pi fa-fw"; - public static readonly string Ravelry = "fab fa-ravelry fa-fw"; - public static readonly string React = "fab fa-react fa-fw"; - public static readonly string Reacteurope = "fab fa-reacteurope fa-fw"; - public static readonly string Readme = "fab fa-readme fa-fw"; - public static readonly string Rebel = "fab fa-rebel fa-fw"; - public static readonly string RedRiver = "fab fa-red-river fa-fw"; - public static readonly string Reddit = "fab fa-reddit fa-fw"; - public static readonly string RedditAlien = "fab fa-reddit-alien fa-fw"; - public static readonly string RedditSquare = "fab fa-reddit-square fa-fw"; - public static readonly string Redhat = "fab fa-redhat fa-fw"; - public static readonly string Renren = "fab fa-renren fa-fw"; - public static readonly string Replyd = "fab fa-replyd fa-fw"; - public static readonly string Researchgate = "fab fa-researchgate fa-fw"; - public static readonly string Resolving = "fab fa-resolving fa-fw"; - public static readonly string Rev = "fab fa-rev fa-fw"; - public static readonly string Rocketchat = "fab fa-rocketchat fa-fw"; - public static readonly string Rockrms = "fab fa-rockrms fa-fw"; - public static readonly string Safari = "fab fa-safari fa-fw"; - public static readonly string Salesforce = "fab fa-salesforce fa-fw"; - public static readonly string Sass = "fab fa-sass fa-fw"; - public static readonly string Schlix = "fab fa-schlix fa-fw"; - public static readonly string Scribd = "fab fa-scribd fa-fw"; - public static readonly string Searchengin = "fab fa-searchengin fa-fw"; - public static readonly string Sellcast = "fab fa-sellcast fa-fw"; - public static readonly string Sellsy = "fab fa-sellsy fa-fw"; - public static readonly string Servicestack = "fab fa-servicestack fa-fw"; - public static readonly string Shirtsinbulk = "fab fa-shirtsinbulk fa-fw"; - public static readonly string Shopware = "fab fa-shopware fa-fw"; - public static readonly string Simplybuilt = "fab fa-simplybuilt fa-fw"; - public static readonly string Sistrix = "fab fa-sistrix fa-fw"; - public static readonly string Sith = "fab fa-sith fa-fw"; - public static readonly string Sketch = "fab fa-sketch fa-fw"; - public static readonly string Skyatlas = "fab fa-skyatlas fa-fw"; - public static readonly string Skype = "fab fa-skype fa-fw"; - public static readonly string Slack = "fab fa-slack fa-fw"; - public static readonly string SlackHash = "fab fa-slack-hash fa-fw"; - public static readonly string Slideshare = "fab fa-slideshare fa-fw"; - public static readonly string Snapchat = "fab fa-snapchat fa-fw"; - public static readonly string SnapchatGhost = "fab fa-snapchat-ghost fa-fw"; - public static readonly string SnapchatSquare = "fab fa-snapchat-square fa-fw"; - public static readonly string Soundcloud = "fab fa-soundcloud fa-fw"; - public static readonly string Sourcetree = "fab fa-sourcetree fa-fw"; - public static readonly string Speakap = "fab fa-speakap fa-fw"; - public static readonly string SpeakerDeck = "fab fa-speaker-deck fa-fw"; - public static readonly string Spotify = "fab fa-spotify fa-fw"; - public static readonly string Squarespace = "fab fa-squarespace fa-fw"; - public static readonly string StackExchange = "fab fa-stack-exchange fa-fw"; - public static readonly string StackOverflow = "fab fa-stack-overflow fa-fw"; - public static readonly string Stackpath = "fab fa-stackpath fa-fw"; - public static readonly string Staylinked = "fab fa-staylinked fa-fw"; - public static readonly string Steam = "fab fa-steam fa-fw"; - public static readonly string SteamSquare = "fab fa-steam-square fa-fw"; - public static readonly string SteamSymbol = "fab fa-steam-symbol fa-fw"; - public static readonly string StickerMule = "fab fa-sticker-mule fa-fw"; - public static readonly string Strava = "fab fa-strava fa-fw"; - public static readonly string Stripe = "fab fa-stripe fa-fw"; - public static readonly string StripeS = "fab fa-stripe-s fa-fw"; - public static readonly string Studiovinari = "fab fa-studiovinari fa-fw"; - public static readonly string Stumbleupon = "fab fa-stumbleupon fa-fw"; - public static readonly string StumbleuponCircle = "fab fa-stumbleupon-circle fa-fw"; - public static readonly string Superpowers = "fab fa-superpowers fa-fw"; - public static readonly string Supple = "fab fa-supple fa-fw"; - public static readonly string Suse = "fab fa-suse fa-fw"; - public static readonly string Swift = "fab fa-swift fa-fw"; - public static readonly string Symfony = "fab fa-symfony fa-fw"; - public static readonly string Teamspeak = "fab fa-teamspeak fa-fw"; - public static readonly string Telegram = "fab fa-telegram fa-fw"; - public static readonly string TelegramPlane = "fab fa-telegram-plane fa-fw"; - public static readonly string TencentWeibo = "fab fa-tencent-weibo fa-fw"; - public static readonly string TheRedYeti = "fab fa-the-red-yeti fa-fw"; - public static readonly string Themeco = "fab fa-themeco fa-fw"; - public static readonly string Themeisle = "fab fa-themeisle fa-fw"; - public static readonly string ThinkPeaks = "fab fa-think-peaks fa-fw"; - public static readonly string TradeFederation = "fab fa-trade-federation fa-fw"; - public static readonly string Trello = "fab fa-trello fa-fw"; - public static readonly string Tripadvisor = "fab fa-tripadvisor fa-fw"; - public static readonly string Tumblr = "fab fa-tumblr fa-fw"; - public static readonly string TumblrSquare = "fab fa-tumblr-square fa-fw"; - public static readonly string Twitch = "fab fa-twitch fa-fw"; - public static readonly string Twitter = "fab fa-twitter fa-fw"; - public static readonly string TwitterSquare = "fab fa-twitter-square fa-fw"; - public static readonly string Typo3 = "fab fa-typo3 fa-fw"; - public static readonly string Uber = "fab fa-uber fa-fw"; - public static readonly string Ubuntu = "fab fa-ubuntu fa-fw"; - public static readonly string Uikit = "fab fa-uikit fa-fw"; - public static readonly string Umbraco = "fab fa-umbraco fa-fw"; - public static readonly string Uniregistry = "fab fa-uniregistry fa-fw"; - public static readonly string Untappd = "fab fa-untappd fa-fw"; - public static readonly string Ups = "fab fa-ups fa-fw"; - public static readonly string Usb = "fab fa-usb fa-fw"; - public static readonly string Usps = "fab fa-usps fa-fw"; - public static readonly string Ussunnah = "fab fa-ussunnah fa-fw"; - public static readonly string Vaadin = "fab fa-vaadin fa-fw"; - public static readonly string Viacoin = "fab fa-viacoin fa-fw"; - public static readonly string Viadeo = "fab fa-viadeo fa-fw"; - public static readonly string ViadeoSquare = "fab fa-viadeo-square fa-fw"; - public static readonly string Viber = "fab fa-viber fa-fw"; - public static readonly string Vimeo = "fab fa-vimeo fa-fw"; - public static readonly string VimeoSquare = "fab fa-vimeo-square fa-fw"; - public static readonly string VimeoV = "fab fa-vimeo-v fa-fw"; - public static readonly string Vine = "fab fa-vine fa-fw"; - public static readonly string Vk = "fab fa-vk fa-fw"; - public static readonly string Vnv = "fab fa-vnv fa-fw"; - public static readonly string Vuejs = "fab fa-vuejs fa-fw"; - public static readonly string Waze = "fab fa-waze fa-fw"; - public static readonly string Weebly = "fab fa-weebly fa-fw"; - public static readonly string Weibo = "fab fa-weibo fa-fw"; - public static readonly string Weixin = "fab fa-weixin fa-fw"; - public static readonly string Whatsapp = "fab fa-whatsapp fa-fw"; - public static readonly string WhatsappSquare = "fab fa-whatsapp-square fa-fw"; - public static readonly string Whmcs = "fab fa-whmcs fa-fw"; - public static readonly string WikipediaW = "fab fa-wikipedia-w fa-fw"; - public static readonly string Windows = "fab fa-windows fa-fw"; - public static readonly string Wix = "fab fa-wix fa-fw"; - public static readonly string WizardsOfTheCoast = "fab fa-wizards-of-the-coast fa-fw"; - public static readonly string WolfPackBattalion = "fab fa-wolf-pack-battalion fa-fw"; - public static readonly string Wordpress = "fab fa-wordpress fa-fw"; - public static readonly string WordpressSimple = "fab fa-wordpress-simple fa-fw"; - public static readonly string Wpbeginner = "fab fa-wpbeginner fa-fw"; - public static readonly string Wpexplorer = "fab fa-wpexplorer fa-fw"; - public static readonly string Wpforms = "fab fa-wpforms fa-fw"; - public static readonly string Wpressr = "fab fa-wpressr fa-fw"; - public static readonly string Xbox = "fab fa-xbox fa-fw"; - public static readonly string Xing = "fab fa-xing fa-fw"; - public static readonly string XingSquare = "fab fa-xing-square fa-fw"; - public static readonly string YCombinator = "fab fa-y-combinator fa-fw"; - public static readonly string Yahoo = "fab fa-yahoo fa-fw"; - public static readonly string Yammer = "fab fa-yammer fa-fw"; - public static readonly string Yandex = "fab fa-yandex fa-fw"; - public static readonly string YandexInternational = "fab fa-yandex-international fa-fw"; - public static readonly string Yarn = "fab fa-yarn fa-fw"; - public static readonly string Yelp = "fab fa-yelp fa-fw"; - public static readonly string Yoast = "fab fa-yoast fa-fw"; - public static readonly string Youtube = "fab fa-youtube fa-fw"; - public static readonly string YoutubeSquare = "fab fa-youtube-square fa-fw"; - public static readonly string Zhihu = "fab fa-zhihu fa-fw"; -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Modals/DataModal.razor b/src/AuthJanitor.AspNet.AdminUi/Modals/DataModal.razor deleted file mode 100644 index 72e9357..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Modals/DataModal.razor +++ /dev/null @@ -1,57 +0,0 @@ - - - - - @Title - - - - @ChildContent - - - - - - - - -@code { - [Parameter] - public string Title { get; set; } = "Modal Dialog"; - - [Parameter] - public string YesButton { get; set; } = "Accept"; - - [Parameter] - public string NoButton { get; set; } = "Close"; - - [Parameter] - public bool Visible { get; set; } - - [Parameter] - public EventCallback VisibleChanged { get; set; } - - [Parameter] - public RenderFragment ChildContent { get; set; } - - [Parameter] - public EventCallback ResultClicked { get; set; } - - protected void YesButtonClicked() - { - ResultClicked.InvokeAsync(true); - Visible = false; - VisibleChanged.InvokeAsync(Visible); - } - protected void NoButtonClicked() - { - ResultClicked.InvokeAsync(false); - Visible = false; - VisibleChanged.InvokeAsync(Visible); - } - protected void CloseButtonClicked() - { - Visible = false; - VisibleChanged.InvokeAsync(Visible); - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Modals/DeleteConfirmationModal.razor b/src/AuthJanitor.AspNet.AdminUi/Modals/DeleteConfirmationModal.razor deleted file mode 100644 index 2b23d4b..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Modals/DeleteConfirmationModal.razor +++ /dev/null @@ -1,28 +0,0 @@ - - - Are you sure you want to delete - @ObjectName - ? - - - -@code { - [Parameter] - public bool Visible { get; set; } - - [Parameter] - public string ObjectName { get; set; } - - [Parameter] - public Guid ObjectId { get; set; } - - [Parameter] - public EventCallback VisibleChanged { get; set; } - - [Parameter] - public EventCallback ResultClicked { get; set; } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/AccessManagement.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/AccessManagement.razor deleted file mode 100644 index 6307498..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/AccessManagement.razor +++ /dev/null @@ -1,158 +0,0 @@ -@page "/accessManagement" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The Access Management tool allows administrators with management access to the identity service being - leveraged to add, remove, and list users who have access to the AuthJanitor tool at various permissions levels. - - - A user may have multiple roles assigned to them, but when using Global Administrator, all system - rights are implicitly granted. - - - - - - - - - - - - -@using Newtonsoft.Json -@code { - protected IEnumerable Users { get; set; } = new List(); - - protected bool CreateModalShowing { get; set; } - protected bool DeleteRoleModalShowing { get; set; } - protected bool DeleteUserModalShowing { get; set; } - protected bool ContextualHelpVisible { get; set; } - - [Parameter] - public AuthJanitorAuthorizedUserViewModel SelectedValue { get; set; } = new AuthJanitorAuthorizedUserViewModel(); - - [Parameter] - public EventCallback SelectedValueChanged { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - Users = await Http.AJList(); - } - - protected void CreateNew() - { - SelectedValue = new AuthJanitorAuthorizedUserViewModel(); - CreateModalShowing = true; - } - - protected async Task CreateCallback(bool result) - { - if (result) - { - await Http.AJCreate(SelectedValue); - await LoadData(); - } - CreateModalShowing = false; - } - - protected async Task DeleteRoleConfirmCallback(bool result) - { - if (result) - { - await Http.PostAsync($"/api/access/removeRole", new StringContent(JsonConvert.SerializeObject(SelectedValue))); - await LoadData(); - } - DeleteRoleModalShowing = false; - } - - protected async Task DeleteUserConfirmCallback(bool result) - { - if (result) - { - await Http.PostAsync($"/api/access/removeUser", new StringContent(JsonConvert.SerializeObject(SelectedValue))); - await LoadData(); - } - DeleteUserModalShowing = false; - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/Dashboard.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/Dashboard.razor deleted file mode 100644 index 8f2748b..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/Dashboard.razor +++ /dev/null @@ -1,128 +0,0 @@ -@page "/" -@page "/dashboard" -@using AuthJanitor.UI.Components - - - - - - - - - - - - - - @* --- TODO --- *@ - - - - - @if (!Metrics.ExpiringSoon.Any()) - { - - - - There are no secrets about to expire! 👍 - - - } -else -{ - - - - - Secrets Expiring Soon - - - - - - - - - - - -} - - -

The AuthJanitor Dashboard shows a brief look at the most important parts of your key and secret management system.

-
-
- - - -@using AuthJanitor.UI.Shared.ViewModels -@code { protected DashboardMetricsViewModel Metrics { get; set; } = new DashboardMetricsViewModel(); - - protected bool ContextualHelpVisible { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() => Metrics = await Http.AJGet(); - - protected Blazorise.Charts.ChartData GetRiskDataSet() - { - return new Blazorise.Charts.ChartData() - { - Datasets = new List>() -{ -new Blazorise.Charts.ChartDataset() -{ -Label = "Risks", -BackgroundColor = new List() { "#1cc88a", "#858796", "#4e73df", "#f6c23e", "#e74a3b" }, -BorderColor = new List() { "#ccc", "#ccc", "#ccc", "#ccc", "#ccc" }, -BorderWidth = 1, -Data = new List() -{ -Metrics.Risk0, Metrics.Risk35, Metrics.Risk60, Metrics.Risk85, Metrics.RiskOver85 -} -} - }, - Labels = new List() { "0", "1-35", "36-60", "61-85", ">85" } - }; - } - protected Blazorise.Charts.ChartData GetExpiryDataSet() - { - return new Blazorise.Charts.ChartData() - { - Datasets = new List>() -{ -new Blazorise.Charts.ChartDataset() -{ -Label = "Risks", -BackgroundColor = new List() { "#1cc88a", "#f6c23e", "#e74a3b" }, -BorderColor = new List() { "#ccc", "#ccc", "#ccc" }, -BorderWidth = 1, -Data = new List() -{ -Metrics.TotalSecrets - Metrics.TotalExpired - Metrics.TotalExpiringSoon, -Metrics.TotalExpiringSoon, -Metrics.TotalExpired -} -} -}, - Labels = new List() { "Valid", "Expiring Soon", "Expired" } - }; - } } diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/IntegrityReports.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/IntegrityReports.razor deleted file mode 100644 index 7fbc747..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/IntegrityReports.razor +++ /dev/null @@ -1,142 +0,0 @@ -@page "/integrityReports" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Since AuthJanitor runs your key/secret management infrastructure, it's important to know that AuthJanitor itself is - secure. In order to accomplish this, several metrics are collected about all of the libraries loaded into the - environment. SHA hashes of each library are provided and any libraries which support Authenticode signatures can - have their certificate chains validated and checked against active revocation lists. -

-

- If you don't quite understand this screen, that's fine; it's not necessary to run AuthJanitor. But if you're very - aware of your infrastructure security posture, it might be worth beginning to track these values to make sure an - attacker can't modify AuthJanitor surreptitiously. -

-
-
- - - -@using AuthJanitor.Integrity -@code { - protected IEnumerable IntegrityReportList { get; set; } = new List(); - protected bool ContextualHelpVisible { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - IntegrityReportList = Newtonsoft.Json.JsonConvert.DeserializeObject>( - await (await Http.GetAsync("api/system/integrityReports")).Content.ReadAsStringAsync()); - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/ManagedSecretDetail.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/ManagedSecretDetail.razor deleted file mode 100644 index 2060703..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/ManagedSecretDetail.razor +++ /dev/null @@ -1,181 +0,0 @@ -@page "/managedSecrets/{SecretId}" - - - - - - - @Secret.Name - @Secret.Description - - - - - - - Services Affected - - - - @foreach (var item in this.Secret.Resources) - { -
@((MarkupString)item.Provider.Details.SvgImage)
- } -
-
- - - - - Last Changed - - - - - @Secret.LastChanged - - - - - - Expires - - - - - @Secret.Expiry - - - - - - - - - - - - - - - - - - - - - @foreach (var resource in Secret.Resources) - { - - - - - - @((MarkupString)resource.Provider.Details.SvgImage) - - - - @resource.Name - - - - - @resource.Description - - - - - @resource.RuntimeDescription - - - - - - - - - - - - - - - - - } - - - - Secrets are the combination of multiple Resources which work together to provide - services to an application. A Secret typically consists of at least one Rekeyable Object Provider - and one Application Lifecycle Provider, although a Rekeyable Object Provider can be a Secret by - itself as well. - - - A Secret has a Rekeying Confirmation mode, which represents how the rekeying is - performed. With Administrator-oriented modes, a human administrator must authenticate to the AuthJanitor system and - approve the rekeying. By doing this, an audit trail is created which leads to the human being who decided to approve - the action. - - -
- - - - - -@code { - public ManagedSecretViewModel Secret { get; set; } = new ManagedSecretViewModel(); - - protected bool CreateModalShowing { get; set; } - protected bool DeleteModalShowing { get; set; } - protected bool ContextualHelpVisible { get; set; } - - [Parameter] - public string SecretId { get; set; } - - public TimeSpan DurationSoFar => DateTimeOffset.UtcNow - Secret.LastChanged.GetValueOrDefault(); - protected IEnumerable _providers; - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - _providers = await Http.AJList(); - Secret = await Http.AJGet(Guid.Parse(SecretId)); - await Task.WhenAll(Secret.Resources.Select(async resource => - { - resource.ProviderConfiguration = await Http.AJGet(resource.ProviderType); - resource.ProviderConfiguration.SerializedConfiguration = resource.SerializedProviderConfiguration; - })); - } - - protected async Task DeleteConfirmCallback(bool result) - { - if (result) - { - await Http.AJDelete(Secret.ObjectId); - NavigationManager.NavigateTo("/managedSecrets"); - } - DeleteModalShowing = false; - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/ManagedSecrets.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/ManagedSecrets.razor deleted file mode 100644 index c15ca33..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/ManagedSecrets.razor +++ /dev/null @@ -1,170 +0,0 @@ -@page "/managedSecrets" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Secrets are the combination of multiple Resources which work together to provide - services to an application. A Secret typically consists of at least one Rekeyable Object Provider - and one Application Lifecycle Provider, although a Rekeyable Object Provider can be a Secret by - itself as well. -

-

- A Secret has a Rekeying Confirmation mode, which represents how the rekeying is - performed. With Administrator-oriented modes, a human administrator must authenticate to the AuthJanitor system and - approve the rekeying. By doing this, an audit trail is created which leads to the human being who decided to approve - the action. -

-
-
- - - - - - - - -@code -{ - protected IEnumerable Secrets { get; set; } = new List(); - - protected bool CreateModalShowing { get; set; } - protected bool DeleteModalShowing { get; set; } - protected bool ContextualHelpVisible { get; set; } - - [Parameter] - public ManagedSecretViewModel SelectedValue { get; set; } = new ManagedSecretViewModel(); - - [Parameter] - public EventCallback SelectedValueChanged { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - Secrets = await Http.AJList(); - await Task.WhenAll(Secrets.SelectMany(s => s.Resources).Distinct().Select(async resource => - { - resource.ProviderConfiguration = await Http.AJGet(resource.ProviderType); - resource.ProviderConfiguration.SerializedConfiguration = resource.SerializedProviderConfiguration; - })); - } - - protected void CreateNew() - { - SelectedValue = new ManagedSecretViewModel(); - CreateModalShowing = true; - } - - protected async Task CreateCallback(bool result) - { - if (result) - { - await Http.AJCreate(SelectedValue); - await LoadData(); - } - CreateModalShowing = false; - } - - protected async Task DeleteConfirmCallback(bool result) - { - if (result) - { - await Http.AJDelete(SelectedValue.ObjectId); - await LoadData(); - } - DeleteModalShowing = false; - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/ProviderSuggestions.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/ProviderSuggestions.razor deleted file mode 100644 index d6fc197..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/ProviderSuggestions.razor +++ /dev/null @@ -1,104 +0,0 @@ -@page "/providers/suggestions" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Resource Suggestions are derived from your currently logged in user's Azure services. AuthJanitor - will scan all available services for ones which are supported by a loaded Provider and suggest - possible configurations for new Resources here. -

-
-
- - - -@using AuthJanitor.ViewModels -@code { - protected IEnumerable Suggestions { get; set; } = new List(); - - private IEnumerable _providers = new LoadedProviderViewModel[0]; - protected bool ContextualHelpVisible { get; set; } - - protected override async Task OnInitializedAsync() - { - _providers = await Http.AJList(); - } - - protected async Task Enumerate() - { - Suggestions = await Http.AJList("enumerate"); - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/Providers.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/Providers.razor deleted file mode 100644 index a419fdb..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/Providers.razor +++ /dev/null @@ -1,72 +0,0 @@ -@page "/providers" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A Provider is a software library for AuthJanitor which implements the logic necessary to either rekey - a service or object, or consume a secret or key to facilitate the operation of an application. - - - Providers each have their own set of distinct configuration options based on the needs of the service. - - - - - - -@using AuthJanitor.UI.Shared.ViewModels; -@code { - protected IEnumerable ProviderList { get; set; } = new List(); - protected bool ContextualHelpVisible { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - ProviderList = await Http.AJList(); - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/RekeyingTaskDetail.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/RekeyingTaskDetail.razor deleted file mode 100644 index d364e1e..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/RekeyingTaskDetail.razor +++ /dev/null @@ -1,189 +0,0 @@ -@page "/rekeyingTasks/{RekeyingTaskId}" - - - - - - - - - - - - @Task.ManagedSecret.Name - - - - - - - - - - Last Changed - - - @Task.ManagedSecret.LastChanged - - - - Expires - - - @Task.Expiry - - - - - - - - - - - - - - - - - - - @if (Task.Attempts.Any()) - { - - - - - @foreach (var attempt in Task.Attempts) - { - - @if (attempt.HasBeenExecuted && attempt.HasBeenExecutedSuccessfully) - { - - } - else if (attempt.HasBeenExecuted && !attempt.HasBeenExecutedSuccessfully) - { - - } - else - { - - } - @attempt.StartedExecution.ToString() - - } - - - @foreach (var attempt in Task.Attempts) - { - - -
@attempt.OrchestrationLog
-
- } -
-
-
-
- } - - - - Rekeying Tasks are created, either automatically by the system as a key or secret nears its expiry, - or manually by an administrator. A Rekeying Task is associated with a single - Secret. Rekeying Tasks can have multiple attempts by different administrators - or service accounts. - - -
- - - - - - - - -@using AuthJanitor.UI.Shared.ViewModels -@code { - public ManagedSecretViewModel Secret => Task == null ? new ManagedSecretViewModel() : Task.ManagedSecret; - public RekeyingTaskViewModel Task { get; set; } = new RekeyingTaskViewModel(); - - protected bool ApproveModalShowing { get; set; } - protected bool DeleteModalShowing { get; set; } - public bool ContextualHelpVisible { get; set; } - - public ProviderWorkflowActionCollection Attempt { get; set; } - - [Parameter] - public string RekeyingTaskId { get; set; } - - protected override async Task OnInitializedAsync() - { - await LoadData(); - } - - protected async Task LoadData() - { - Task = await Http.AJGet(Guid.Parse(RekeyingTaskId)); - if (Task.Attempts.Any()) - { - SelectedAttemptTab = Task.Attempts.OrderByDescending(a => a.StartedExecution).FirstOrDefault()?.StartedExecution.ToString(); - Attempt = Task.Attempts.OrderByDescending(a => a.FinishedExecution).FirstOrDefault(); - } - - //await System.Threading.Tasks.Task.WhenAll(Task.ManagedSecret.Resources.Select(async resource => - //{ - // resource.ProviderConfiguration = await Http.AJGet(resource.ProviderType); - // resource.ProviderConfiguration.SerializedConfiguration = resource.SerializedProviderConfiguration; - //})); - } - - string SelectedAttemptTab; - private void OnSelectedTabChanged(string name) - { - SelectedAttemptTab = name; - Attempt = Task.Attempts.FirstOrDefault(a => a.StartedExecution.ToString() == name); - StateHasChanged(); - } - - protected async Task ApproveCallback(bool result) - { - if (result) - { - await Http.PostAsync($"/api/tasks/{Task.ObjectId}/approve", new StringContent(string.Empty)); - await LoadData(); - } - ApproveModalShowing = false; - } - - protected async Task DeleteConfirmCallback(bool result) - { - if (result) - { - await Http.AJDelete(Task.ObjectId); - NavigationManager.NavigateTo("/rekeyingTasks"); - } - DeleteModalShowing = false; - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/RekeyingTasks.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/RekeyingTasks.razor deleted file mode 100644 index bea3f80..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/RekeyingTasks.razor +++ /dev/null @@ -1,158 +0,0 @@ -@page "/rekeyingTasks" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Rekeying Tasks are created, either automatically by the system as a key or secret nears its expiry, - or manually by an administrator. A Rekeying Task is associated with a single - Secret. Rekeying Tasks can have multiple attempts by different administrators - or service accounts. - - - - - - - - - - - -@using AuthJanitor.UI.Modals -@using AuthJanitor.UI.Shared.ViewModels -@code { - protected IEnumerable Tasks { get; set; } = new List(); - - protected bool ApproveModalShowing { get; set; } - protected bool DeleteModalShowing { get; set; } - protected bool ContextualHelpVisible { get; set; } - - [Parameter] - public RekeyingTaskViewModel SelectedValue { get; set; } = new RekeyingTaskViewModel(); - - [Parameter] - public EventCallback SelectedValueChanged { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() => Tasks = await Http.AJList(); - - protected async Task ApproveCallback(bool result) - { - if (result) - { - await Http.PostAsync($"/api/tasks/{SelectedValue.ObjectId}/approve", new StringContent(string.Empty)); - await LoadData(); - } - ApproveModalShowing = false; - } - - protected async Task DeleteConfirmCallback(bool result) - { - if (result) - { - await Http.AJDelete(SelectedValue.ObjectId); - await LoadData(); - } - DeleteModalShowing = false; - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/ResourceDetail.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/ResourceDetail.razor deleted file mode 100644 index 7e51f55..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/ResourceDetail.razor +++ /dev/null @@ -1,143 +0,0 @@ -@page "/resources/{ResourceId}" - - - - - - @if (_provider != null) - { - -
@((MarkupString)_provider.Details.SvgImage)
-
@_provider.Details.Name
-
- } - - @Resource.Name - @Resource.Description - -
- - - - @Resource.RuntimeDescription - - - - - - - - - - - - - - - - @if (Resource.Risks.Any()) - { - - - - - Risk - Remediation - - - @foreach (var risk in Resource.Risks) - { - - - - - - @risk.Score - - - - - - @risk.Risk - - - - @risk.Recommendation - - - } - - - - } - - - - - - - - - - Resources describe the configuration required to access a given service or object for the purposes - of either delivering key/secret material or rekeying that object or service. A Resource is made up - of a Provider and a corresponding Provider Configuration, which is a structure - defined by the Provider library. - - - -
- - - - - -@using AuthJanitor.UI.Shared.ViewModels -@code { - [Parameter] - public string ResourceId { get; set; } - - protected bool DeleteModalShowing { get; set; } - protected bool ContextualHelpVisible { get; set; } - - public ResourceViewModel Resource { get; set; } = new ResourceViewModel(); - protected LoadedProviderViewModel _provider; - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - Resource = await Http.AJGet(Guid.Parse(ResourceId)); - var providers = await Http.AJList(); - _provider = providers.First(p => p.ProviderTypeName == Resource.ProviderType); - Resource.ProviderConfiguration = await Http.AJGet(Resource.ProviderType); - Resource.ProviderConfiguration.SerializedConfiguration = Resource.SerializedProviderConfiguration; - } - - protected async Task DeleteConfirmCallback(bool result) - { - if (result) - { - await Http.AJDelete(Resource.ObjectId); - await LoadData(); - } - DeleteModalShowing = false; - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Pages/Resources.razor b/src/AuthJanitor.AspNet.AdminUi/Pages/Resources.razor deleted file mode 100644 index 8e9562b..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Pages/Resources.razor +++ /dev/null @@ -1,150 +0,0 @@ -@page "/resources" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Resources describe the configuration required to access a given service or object for the purposes - of either delivering key/secret material or rekeying that object or service. A Resource is made up - of a Provider and a corresponding Provider Configuration, which is a structure - defined by the Provider library. -

-
-
- - - - - - - - -@using AuthJanitor.UI.Editors -@using AuthJanitor.UI.Modals -@code { - protected IEnumerable ManagedResources { get; set; } = new List(); - - protected bool CreateModalShowing { get; set; } - protected bool DeleteModalShowing { get; set; } - protected bool ContextualHelpVisible { get; set; } - - [Parameter] - public ResourceViewModel SelectedValue { get; set; } = new ResourceViewModel(); - - [Parameter] - public EventCallback SelectedValueChanged { get; set; } - - protected override async Task OnInitializedAsync() => await LoadData(); - - protected async Task LoadData() - { - ManagedResources = await Http.AJList(); - await Task.WhenAll(ManagedResources.Select(async resource => - { - resource.ProviderConfiguration = await Http.AJGet(resource.ProviderType); - resource.ProviderConfiguration.SerializedConfiguration = resource.SerializedProviderConfiguration; - })); - } - - protected void CreateNew() - { - SelectedValue = new ResourceViewModel(); - CreateModalShowing = true; - } - - protected async Task CreateCallback(bool result) - { - if (result) - { - SelectedValue.SerializedProviderConfiguration = SelectedValue.ProviderConfiguration?.SerializedConfiguration; - await Http.AJCreate(SelectedValue); - await LoadData(); - } - CreateModalShowing = false; - } - - protected async Task DeleteConfirmCallback(bool result) - { - if (result) - { - await Http.AJDelete(SelectedValue.ObjectId); - await LoadData(); - } - DeleteModalShowing = false; - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Program.cs b/src/AuthJanitor.AspNet.AdminUi/Program.cs deleted file mode 100644 index e33a904..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Program.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared; -using Blazorise; -using Blazorise.Bootstrap; -using Blazorise.Icons.FontAwesome; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Threading.Tasks; - -namespace AuthJanitor.UI -{ - public class Program - { - public static async Task Main(string[] args) - { - var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.Services - .AddBlazorise(options => options.ChangeTextOnKeyPress = true) - .AddBootstrapProviders() - .AddFontAwesomeIcons(); - - builder.Services.AddHttpClient( - client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); - - builder.RootComponents.Add("app"); - - var host = builder.Build(); - - host.Services - .UseBootstrapProviders() - .UseFontAwesomeIcons(); - - await host.RunAsync(); - } - } -} diff --git a/src/AuthJanitor.AspNet.AdminUi/Properties/PublishProfiles/FolderProfile.pubxml b/src/AuthJanitor.AspNet.AdminUi/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 30fee13..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - False - False - True - Release - Any CPU - FileSystem - bin\Release\netstandard2.1\publish\ - FileSystem - - \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Properties/launchSettings.json b/src/AuthJanitor.AspNet.AdminUi/Properties/launchSettings.json deleted file mode 100644 index 4fe8fee..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Properties/launchSettings.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:16100/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchUrl": "http://localhost:16000/", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" - }, - "AuthJanitor.AspNet.AdminUi": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:16000/", - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/Shared/MainLayout.razor b/src/AuthJanitor.AspNet.AdminUi/Shared/MainLayout.razor deleted file mode 100644 index 95b3bf0..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/Shared/MainLayout.razor +++ /dev/null @@ -1,127 +0,0 @@ -@inherits LayoutComponentBase - -
- @* --- Main Navbar Area --- *@ - - - @* --- Content Area / Footer Area --- *@ - @Body -
- -@using System.Security.Cryptography -@using System.Text -@inject IJSRuntime JSRuntime -@code -{ protected DashboardMetricsViewModel Metrics { get; set; } = new DashboardMetricsViewModel(); - - protected override async Task OnInitializedAsync() - { - Metrics = await Http.AJGet(); - } - - protected string GetGravatar() - { - if (Metrics == null || string.IsNullOrEmpty(Metrics.SignedInEmail)) - return string.Empty; - return $"https://www.gravatar.com/avatar/{MD5Hash(Metrics.SignedInEmail)}.jpg"; - } - - private static string MD5Hash(string input) - { - // This is *not* FIPS-compliant - StringBuilder hash = new StringBuilder(); - byte[] bytes = MD5.Create().ComputeHash(new UTF8Encoding().GetBytes(input)); - - for (int i = 0; i < bytes.Length; i++) - { - hash.Append(bytes[i].ToString("x2")); - } - return hash.ToString(); - } } \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/_Imports.razor b/src/AuthJanitor.AspNet.AdminUi/_Imports.razor deleted file mode 100644 index 080968c..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/_Imports.razor +++ /dev/null @@ -1,20 +0,0 @@ -@using System.Net.Http -@using Microsoft.AspNetCore.Components.Forms -@using Microsoft.AspNetCore.Components.Routing -@using Microsoft.AspNetCore.Components.Web -@using Microsoft.JSInterop - -@using AuthJanitor.UI.Components -@using AuthJanitor.UI.Editors -@using AuthJanitor.UI.Modals - -@using AuthJanitor.UI.Shared -@using AuthJanitor.UI.Shared.ViewModels -@using AuthJanitor.Providers - -@* Adds support for grabbing info from REST APIs *@ -@inject AuthJanitorHttpClient Http -@* Component library *@ -@using Blazorise -@* Adds support for navigating from code *@ -@inject Microsoft.AspNetCore.Components.NavigationManager NavigationManager \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/browserconfig.xml b/src/AuthJanitor.AspNet.AdminUi/wwwroot/browserconfig.xml deleted file mode 100644 index c049992..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/wwwroot/browserconfig.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - #ffffff - - - \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/AJAnimated.svg b/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/AJAnimated.svg deleted file mode 100644 index ea4ee4e..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/AJAnimated.svg +++ /dev/null @@ -1,2 +0,0 @@ - \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/logo.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/logo.png deleted file mode 100644 index c1248d3..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/logo.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/style.css b/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/style.css deleted file mode 100644 index aec9302..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/wwwroot/css/style.css +++ /dev/null @@ -1,372 +0,0 @@ -/* Copyright (c) Microsoft Corporation. */ -/* Licensed under the MIT license. */ -.app-main { - width: 100vw; - height: 100vh; -} - -.footer { - position: fixed; - bottom: 0; - width: 100%; - line-height: 2em; - background-color: #f5f5f5; -} - - .footer .container-fluid .row { - justify-content: space-between; - } - -#helpContainer { - -webkit-transition: right 1s ease; - -moz-transition: right 1s ease; - -o-transition: right 1s ease; - transition: right 1s ease; - padding: 1em 2em; - border: 1px solid transparent; - border-radius: 10px 0px 0px 10px; - width: 40vw; - background-color: rgb(0, 0, 0, 0.7); - position: fixed; - right: -40vw; - overflow: hidden; - top: 50%; - transform: translate(0%, -50%); - border-width: 0.25em; - backdrop-filter: blur(6px); -} - - #helpContainer.in { - right: 0; - } - -.card { - overflow: hidden; -} - -.card-body .rotate { - z-index: 8; - float: right; - height: 100%; -} - - .card-body .rotate svg { - color: rgba(20, 20, 20, 0.15); - position: absolute; - left: 0; - left: auto; - right: -20px; - bottom: -5px; - display: block; - -webkit-transform: rotate(-44deg); - -moz-transform: rotate(-44deg); - -o-transform: rotate(-44deg); - -ms-transform: rotate(-44deg); - transform: rotate(-44deg); - } - -/* Confirmation Strategy Groups */ -span.confirmationGroup { - position: relative; - border-radius: 0.25rem 0.25rem 0.25rem 0rem; - border-width: 2px !important; - padding-bottom: 12px !important; -} - - span.confirmationGroup .groupTitle { - font-size: 11px; - position: absolute; - bottom: -1.6em; - left: -0.2em; - padding: 0.2em 0.3em; - border-radius: 0rem 0rem 0.25rem 0.25rem; - } - -/* "Group Box" Headers */ -div.container.hasSectionHeader { - border-top: 1px solid #ccc; - margin-top: 1.25em; - position: relative; - padding-top: 1em; -} - -span.sectionHeader { - position: absolute; - top: -1.5em; - left: 0.4em; - color: #666; - background-color: #fafafa; - border: 1px solid #ccc; - padding: 0.25em 0.5em; -} - -.providerImage { - -ms-flex: 0 0 6em; - flex: 0 0 6em; -} - -/* -------------------------------------------------------------------------------- */ - - - - - -html, body { - font-family: 'Nunito', 'Helvetica Neue', Helvetica, Arial, sans-serif; -} - -body app { - position: relative; - display: flex; - align-items: stretch; -} - -/*.app-main { - width: 100%; -}*/ - -.app-content { - padding: 1rem !important; - /*padding-right: 1rem !important; - padding-left: 1rem !important;*/ -} - -.border-left-primary { - border-left: 0.25rem solid #4e73df !important; -} - -.border-bottom-primary { - border-bottom: 0.25rem solid #4e73df !important; -} - -.border-left-secondary { - border-left: 0.25rem solid #858796 !important; -} - -.border-bottom-secondary { - border-bottom: 0.25rem solid #858796 !important; -} - -.border-left-success { - border-left: 0.25rem solid #1cc88a !important; -} - -.border-bottom-success { - border-bottom: 0.25rem solid #1cc88a !important; -} - -.border-left-info { - border-left: 0.25rem solid #36b9cc !important; -} - -.border-bottom-info { - border-bottom: 0.25rem solid #36b9cc !important; -} - -.border-left-warning { - border-left: 0.25rem solid #f6c23e !important; -} - -.border-bottom-warning { - border-bottom: 0.25rem solid #f6c23e !important; -} - -.border-left-danger { - border-left: 0.25rem solid #e74a3b !important; -} - -.border-bottom-danger { - border-bottom: 0.25rem solid #e74a3b !important; -} - -.border-left-light { - border-left: 0.25rem solid #f8f9fc !important; -} - -.border-bottom-light { - border-bottom: 0.25rem solid #f8f9fc !important; -} - -.border-left-dark { - border-left: 0.25rem solid #5a5c69 !important; -} - -.border-bottom-dark { - border-bottom: 0.25rem solid #5a5c69 !important; -} - -.bg-gradient-primary { - background-color: #4e73df; - background-image: linear-gradient(180deg, #4e73df 10%, #224abe 100%); - background-size: cover; -} - -.bg-gradient-secondary { - background-color: #858796; - background-image: linear-gradient(180deg, #858796 10%, #60616f 100%); - background-size: cover; -} - -.bg-gradient-success { - background-color: #1cc88a; - background-image: linear-gradient(180deg, #1cc88a 10%, #13855c 100%); - background-size: cover; -} - -.bg-gradient-info { - background-color: #36b9cc; - background-image: linear-gradient(180deg, #36b9cc 10%, #258391 100%); - background-size: cover; -} - -.bg-gradient-warning { - background-color: #f6c23e; - background-image: linear-gradient(180deg, #f6c23e 10%, #dda20a 100%); - background-size: cover; -} - -.bg-gradient-danger { - background-color: #e74a3b; - background-image: linear-gradient(180deg, #e74a3b 10%, #be2617 100%); - background-size: cover; -} - -.bg-gradient-light { - background-color: #f8f9fc; - background-image: linear-gradient(180deg, #f8f9fc 10%, #c2cbe5 100%); - background-size: cover; -} - -.bg-gradient-dark { - background-color: #5a5c69; - background-image: linear-gradient(180deg, #5a5c69 10%, #373840 100%); - background-size: cover; -} - -.bg-gray-100 { - background-color: #f8f9fc !important; -} - -.bg-gray-200 { - background-color: #eaecf4 !important; -} - -.bg-gray-300 { - background-color: #dddfeb !important; -} - -.bg-gray-400 { - background-color: #d1d3e2 !important; -} - -.bg-gray-500 { - background-color: #b7b9cc !important; -} - -.bg-gray-600 { - background-color: #858796 !important; -} - -.bg-gray-700 { - background-color: #6e707e !important; -} - -.bg-gray-800 { - background-color: #5a5c69 !important; -} - -.bg-gray-900 { - background-color: #3a3b45 !important; -} - -.text-gray-100 { - color: #f8f9fc !important; -} - -.text-gray-200 { - color: #eaecf4 !important; -} - -.text-gray-300 { - color: #dddfeb !important; -} - -.text-gray-400 { - color: #d1d3e2 !important; -} - -.text-gray-500 { - color: #b7b9cc !important; -} - -.text-gray-600 { - color: #858796 !important; -} - -.text-gray-700 { - color: #6e707e !important; -} - -.text-gray-800 { - color: #5a5c69 !important; -} - -.text-gray-900 { - color: #3a3b45 !important; -} - -.text-xs { - font-size: .7rem; -} - -.text-lg { - font-size: 1.2rem; -} - -.loader { - width: 200px; - height: 200px; - position: relative; - top: 1vh; - left: 0; - right: 0; - bottom: 0; - margin: auto; - border-radius: 50%; - box-shadow: 0 0 5px #444; -} - - .loader:before { - color: #305a70; - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border-radius: 50%; - background: linear-gradient(#6666ff, black 60%); - animation: spin .5s infinite linear; - } - - .loader:after { - color: #305a70; - content: ""; - position: absolute; - width: 95%; - height: 95%; - top: 2.5%; - left: 2.5%; - background-color: #305a70; - border-radius: 50%; - box-shadow: inset 0 0 5px #999; - } - .loader i:before { } - .loader i:after { - } - -@keyframes spin { - to { - transform: rotate(360deg); - } -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/favicon.ico b/src/AuthJanitor.AspNet.AdminUi/wwwroot/favicon.ico deleted file mode 100644 index a52901e..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/favicon.ico and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-144x144.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-144x144.png deleted file mode 100644 index da25bbb..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-144x144.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-192x192.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-192x192.png deleted file mode 100644 index a306376..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-192x192.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-36x36.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-36x36.png deleted file mode 100644 index b807a83..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-36x36.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-48x48.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-48x48.png deleted file mode 100644 index 73bc79d..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-48x48.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-72x72.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-72x72.png deleted file mode 100644 index efb9c26..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-72x72.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-96x96.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-96x96.png deleted file mode 100644 index 001d978..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/android-icon-96x96.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-114x114.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-114x114.png deleted file mode 100644 index a677d15..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-114x114.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-120x120.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-120x120.png deleted file mode 100644 index 0bea4c7..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-120x120.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-144x144.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-144x144.png deleted file mode 100644 index da25bbb..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-144x144.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-152x152.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-152x152.png deleted file mode 100644 index 5fbb421..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-152x152.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-180x180.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-180x180.png deleted file mode 100644 index db5702a..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-180x180.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-57x57.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-57x57.png deleted file mode 100644 index d567277..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-57x57.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-60x60.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-60x60.png deleted file mode 100644 index 7ce3967..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-60x60.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-72x72.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-72x72.png deleted file mode 100644 index efb9c26..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-72x72.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-76x76.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-76x76.png deleted file mode 100644 index 27e7b51..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-76x76.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-precomposed.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-precomposed.png deleted file mode 100644 index 04ae659..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon-precomposed.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon.png deleted file mode 100644 index 04ae659..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/apple-icon.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-16x16.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-16x16.png deleted file mode 100644 index 71b17e6..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-16x16.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-32x32.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-32x32.png deleted file mode 100644 index be269bc..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-32x32.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-96x96.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-96x96.png deleted file mode 100644 index 001d978..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/favicon-96x96.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/manifest.json b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/manifest.json deleted file mode 100644 index 982078e..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/manifest.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "App", - "icons": [ - { - "src": "\/icons\/android-icon-36x36.png", - "sizes": "36x36", - "type": "image\/png", - "density": "0.75" - }, - { - "src": "\/icons\/android-icon-48x48.png", - "sizes": "48x48", - "type": "image\/png", - "density": "1.0" - }, - { - "src": "\/icons\/android-icon-72x72.png", - "sizes": "72x72", - "type": "image\/png", - "density": "1.5" - }, - { - "src": "\/icons\/android-icon-96x96.png", - "sizes": "96x96", - "type": "image\/png", - "density": "2.0" - }, - { - "src": "\/icons\/android-icon-144x144.png", - "sizes": "144x144", - "type": "image\/png", - "density": "3.0" - }, - { - "src": "\/icons\/android-icon-192x192.png", - "sizes": "192x192", - "type": "image\/png", - "density": "4.0" - } - ] -} \ No newline at end of file diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-144x144.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-144x144.png deleted file mode 100644 index da25bbb..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-144x144.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-150x150.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-150x150.png deleted file mode 100644 index ab5644d..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-150x150.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-310x310.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-310x310.png deleted file mode 100644 index 318614e..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-310x310.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-70x70.png b/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-70x70.png deleted file mode 100644 index e2d8e00..0000000 Binary files a/src/AuthJanitor.AspNet.AdminUi/wwwroot/icons/ms-icon-70x70.png and /dev/null differ diff --git a/src/AuthJanitor.AspNet.AdminUi/wwwroot/index.html b/src/AuthJanitor.AspNet.AdminUi/wwwroot/index.html deleted file mode 100644 index 6eb64f0..0000000 --- a/src/AuthJanitor.AspNet.AdminUi/wwwroot/index.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - AuthJanitor Administration Tool - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
- - - - - - - - - - - - - - - - - diff --git a/src/AuthJanitor.AspNet/AuthJanitor.AspNet.Core.csproj b/src/AuthJanitor.AspNet/AuthJanitor.AspNet.Core.csproj deleted file mode 100644 index d38d477..0000000 --- a/src/AuthJanitor.AspNet/AuthJanitor.AspNet.Core.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.1 - Debug;Release - AuthJanitor - - - - - - - - - - - - - diff --git a/src/AuthJanitor.AspNet/AuthJanitorHttpClient.cs b/src/AuthJanitor.AspNet/AuthJanitorHttpClient.cs deleted file mode 100644 index 74095aa..0000000 --- a/src/AuthJanitor.AspNet/AuthJanitorHttpClient.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.ViewModels; -using AuthJanitor.ViewModels; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; - -namespace AuthJanitor.UI.Shared -{ - public class AuthJanitorHttpClient : HttpClient - { - public const string HEADER_NAME = "AuthJanitor"; - public const string HEADER_VALUE = "administrator"; - - private static readonly Dictionary ApiFormatStrings = new Dictionary() - { - { typeof(DashboardMetricsViewModel), "dashboard" }, - { typeof(ManagedSecretViewModel), "managedSecrets" }, - { typeof(RekeyingTaskViewModel), "tasks" }, - { typeof(ResourceViewModel), "resources" }, - { typeof(LoadedProviderViewModel), "providers" }, - { typeof(ProviderConfigurationViewModel), "providers" }, - { typeof(AuthJanitorAuthorizedUserViewModel), "access" }, - { typeof(ProviderResourceSuggestionViewModel), "providers" } - }; - - public AuthJanitorHttpClient() : base() - { - if (!DefaultRequestHeaders.Contains(HEADER_NAME)) - DefaultRequestHeaders.Add(HEADER_NAME, HEADER_VALUE); - } - - public AuthJanitorHttpClient(HttpClient client) : this() - { - BaseAddress = new Uri($"{client.BaseAddress}../api"); - Console.WriteLine($"Creating AuthJanitor REST API Client with BaseAddress {BaseAddress}"); - foreach (var item in client.DefaultRequestHeaders) - DefaultRequestHeaders.Add(item.Key, item.Value); - MaxResponseContentBufferSize = client.MaxResponseContentBufferSize; - Timeout = client.Timeout; - } - - public Task AJGet() where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .GetAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}") - .ContinueWith(t => GetFromContentPayload(t.Result)) - .Unwrap(); - - public Task AJGet(string name) where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .GetAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}/{name}") - .ContinueWith(t => GetFromContentPayload(t.Result)) - .Unwrap(); - - public Task AJGet(Guid objectId) where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .GetAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}/{objectId}") - .ContinueWith(t => GetFromContentPayload(t.Result)) - .Unwrap(); - - public Task> AJList() where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .GetAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}") - .ContinueWith(t => GetFromContentPayload>(t.Result)) - .Unwrap(); - - public Task> AJList(string objectName) where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .GetAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}/{objectName}") - .ContinueWith(t => GetFromContentPayload>(t.Result)) - .Unwrap(); - - public Task AJCreate(T obj) where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .PostAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}", new StringContent(Serialize(obj))) - .ContinueWith(t => GetFromContentPayload(t.Result)) - .Unwrap(); - - public Task AJUpdate(Guid objectId, T obj) where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .PostAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}/{objectId}", new StringContent(Serialize(obj))) - .ContinueWith(t => GetFromContentPayload(t.Result)) - .Unwrap(); - - public Task AJDelete(Guid objectId) where T : IAuthJanitorViewModel => this - .AssertRequestIsSane() - .DeleteAsync($"{BaseAddress}/{ApiFormatStrings[typeof(T)]}/{objectId}") - .ContinueWith(t => t.Result.EnsureSuccessStatusCode()); - - private async Task GetFromContentPayload(HttpResponseMessage message) => - message.Content.Headers.ContentLength == 0 ? default : - Deserialize(await message.Content.ReadAsStringAsync()); - - private AuthJanitorHttpClient AssertRequestIsSane() - { - if (!ApiFormatStrings.ContainsKey(typeof(T))) - throw new Exception("Unsupported data abstraction!"); - return this; - } - - // NOTE: For the moment, we use Newtonsoft with the API, but that limits it to just the Automation. - // When support for System.Text.Json is put into Functions, we can change this back. - private string Serialize(T obj) => JsonConvert.SerializeObject(obj, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); - private T Deserialize(string str) => JsonConvert.DeserializeObject(str, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); - } -} diff --git a/src/AuthJanitor.AspNet/DataStores/IAuthJanitorModel.cs b/src/AuthJanitor.AspNet/DataStores/IAuthJanitorModel.cs deleted file mode 100644 index cbf15f8..0000000 --- a/src/AuthJanitor.AspNet/DataStores/IAuthJanitorModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; - -namespace AuthJanitor.DataStores -{ - public interface IAuthJanitorModel - { - /// - /// Unique Object Identifier - /// - Guid ObjectId { get; set; } - } -} diff --git a/src/AuthJanitor.AspNet/DataStores/IDataStore.cs b/src/AuthJanitor.AspNet/DataStores/IDataStore.cs deleted file mode 100644 index cd254b6..0000000 --- a/src/AuthJanitor.AspNet/DataStores/IDataStore.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.DataStores -{ - public interface IDataStore - where TStoredModel : IAuthJanitorModel - { - /// - /// Store an instance of a model which has a new ObjectId - /// - /// Model to store - Task Create(TStoredModel model, CancellationToken cancellationToken); - - /// - /// Store a new version of a model which already exists - /// - /// Model to store - Task Update(TStoredModel model, CancellationToken cancellationToken); - - /// - /// List all models currently stored - /// - /// List of models currently stored - Task> Get(CancellationToken cancellationToken); - - /// - /// Delete a model by its ObjectId - /// - /// ObjectId to delete - Task Delete(Guid objectId, CancellationToken cancellationToken); - - /// - /// Test if a given ObjectId has been stored - /// - /// ObjectId to test - /// TRUE if the ObjectId is in the DataStore, otherwise FALSE - Task ContainsId(Guid objectId, CancellationToken cancellationToken); - - /// - /// Retrieve a model by its ObjectId - /// - /// ObjectId to retrieve - /// Object described by requested ObjectId - Task GetOne(Guid objectId, CancellationToken cancellationToken); - - /// - /// Retrieve one or more models, filtered by a Predicate condition - /// - /// Predicate used to filter models - /// List of models matching predicate - Task> Get(Func predicate, CancellationToken cancellationToken); - - /// - /// Retrieve the first instance of a model, filtered by a Predicate condition - /// - /// Predicate used to filter models - /// First model matching predicate - Task GetOne(Func predicate, CancellationToken cancellationToken); - } -} diff --git a/src/AuthJanitor.AspNet/Extensions.cs b/src/AuthJanitor.AspNet/Extensions.cs deleted file mode 100644 index 8c654ff..0000000 --- a/src/AuthJanitor.AspNet/Extensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; - -namespace AuthJanitor.UI.Shared -{ - public static class Extensions - { - public static string ToReadableString(this TimeSpan span, bool shortText = false) - { - string formatted = string.Format("{0}{1}{2}{3}{4}", - span.Duration().Days > 30 ? string.Format(shortText ? "~{0:0}m " : "~{0:0} month{1}, ", span.Days / 30, (span.Days / 30) == 1 ? string.Empty : "s") : string.Empty, - span.Duration().Days > 0 ? string.Format(shortText ? "{0:0}d " : "{0:0} day{1}, ", span.Days, span.Days == 1 ? string.Empty : "s") : string.Empty, - span.Duration().Hours > 0 ? string.Format(shortText ? "{0:0}h " : "{0:0} hour{1}, ", span.Hours, span.Hours == 1 ? string.Empty : "s") : string.Empty, - span.Duration().Minutes > 0 ? string.Format(shortText ? "{0:0}m " : "{0:0} minute{1}, ", span.Minutes, span.Minutes == 1 ? string.Empty : "s") : string.Empty, - span.Duration().Seconds > 0 ? string.Format(shortText ? "{0:0}s" : "{0:0} second{1}", span.Seconds, span.Seconds == 1 ? string.Empty : "s") : string.Empty); - - if (formatted.EndsWith(", ")) formatted = formatted[0..^2]; - - if (string.IsNullOrEmpty(formatted)) formatted = "Enter a duration in minutes."; - - return formatted; - } - - public static T GetEnumValueAttribute(this Enum enumVal) where T : Attribute - { - var attrib = enumVal.GetType() - .GetMember(enumVal.ToString())[0] - .GetCustomAttributes(typeof(T), false); - return (attrib.Length > 0) ? (T)attrib[0] : null; - } - } -} diff --git a/src/AuthJanitor.AspNet/Models/ManagedSecret.cs b/src/AuthJanitor.AspNet/Models/ManagedSecret.cs deleted file mode 100644 index 483335b..0000000 --- a/src/AuthJanitor.AspNet/Models/ManagedSecret.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace AuthJanitor.UI.Shared.Models -{ - public class ManagedSecret : IAuthJanitorModel - { - public Guid ObjectId { get; set; } = Guid.NewGuid(); - public string Name { get; set; } - 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; } - - public IEnumerable AdminEmails { get; set; } = new List(); - - public string Nonce { get; set; } - - public IEnumerable ResourceIds { get; set; } = new List(); - - /// - /// If the ManagedSecret is valid - /// - [JsonIgnore] - public bool IsValid => Expiry < DateTimeOffset.UtcNow; - - /// - /// Date/Time of expiry - /// - [JsonIgnore] - public DateTimeOffset Expiry => LastChanged + ValidPeriod; - - /// - /// Time remaining until Expiry (if expired, TimeSpan.Zero) - /// - [JsonIgnore] - public TimeSpan TimeRemaining => IsValid ? Expiry - DateTimeOffset.UtcNow : TimeSpan.Zero; - } -} diff --git a/src/AuthJanitor.AspNet/Models/RekeyingTask.cs b/src/AuthJanitor.AspNet/Models/RekeyingTask.cs deleted file mode 100644 index b1b8f7e..0000000 --- a/src/AuthJanitor.AspNet/Models/RekeyingTask.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.Providers; -using System; -using System.Collections.Generic; - -namespace AuthJanitor.UI.Shared.Models -{ - public class RekeyingTask : IAuthJanitorModel - { - public Guid ObjectId { get; set; } = Guid.NewGuid(); - - public DateTimeOffset Queued { get; set; } - public DateTimeOffset Expiry { get; set; } - - public bool RekeyingInProgress { get; set; } = false; - public bool RekeyingCompleted { get; set; } = false; - public bool RekeyingFailed { get; set; } = false; - - public TaskConfirmationStrategies ConfirmationType { get; set; } - - public string PersistedCredentialUser { get; set; } - public Guid PersistedCredentialId { get; set; } - - public Guid AvailabilityScheduleId { get; set; } - - public Guid ManagedSecretId { get; set; } - - public List Attempts { get; set; } = new List(); - } -} diff --git a/src/AuthJanitor.AspNet/Models/Resource.cs b/src/AuthJanitor.AspNet/Models/Resource.cs deleted file mode 100644 index b2d88bc..0000000 --- a/src/AuthJanitor.AspNet/Models/Resource.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using System; - -namespace AuthJanitor.UI.Shared.Models -{ - public class Resource : IAuthJanitorModel - { - public Guid ObjectId { get; set; } = Guid.NewGuid(); - public string Name { get; set; } - public string Description { get; set; } - - public string ProviderType { get; set; } - public string ProviderConfiguration { get; set; } - } -} diff --git a/src/AuthJanitor.AspNet/Models/ScheduleWindow.cs b/src/AuthJanitor.AspNet/Models/ScheduleWindow.cs deleted file mode 100644 index 7f562c6..0000000 --- a/src/AuthJanitor.AspNet/Models/ScheduleWindow.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using System; -using System.Collections.Generic; -//using System.Linq; - -namespace AuthJanitor.UI.Shared.Models -{ - public class ScheduleWindow : IAuthJanitorModel - { - public Guid ObjectId { get; set; } - - public IEnumerable CronStrings { get; set; } = new List(); - - //[JsonIgnore] - //public DateTimeOffset NextOccurrence => - // CronStrings.Select(s => CronExpression.Parse(s)) - // .Select(c => c.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Utc).GetValueOrDefault()) - // .OrderBy(c => c.Ticks) - // .First(); - } -} diff --git a/src/AuthJanitor.AspNet/TaskConfirmationStrategies.cs b/src/AuthJanitor.AspNet/TaskConfirmationStrategies.cs deleted file mode 100644 index 9fd3e66..0000000 --- a/src/AuthJanitor.AspNet/TaskConfirmationStrategies.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; - -namespace AuthJanitor -{ - [Serializable] - [Flags] - public enum TaskConfirmationStrategies : int - { - None = 0, // fa fa-close - AdminSignsOffJustInTime = 1, // fa fa-pencil - AdminCachesSignOff = 2, // fa fa-sticky-note-o - AutomaticRekeyingAsNeeded = 4, // fa fa-rotate-left - AutomaticRekeyingScheduled = 8, // fa fa-clock-o - ExternalSignal = 16 // fa fa-flag - } - - public static class TaskConfirmationStrategiesExtensions - { - public static bool UsesOBOTokens(this TaskConfirmationStrategies confirmationStrategies) => - confirmationStrategies.HasFlag(TaskConfirmationStrategies.AdminCachesSignOff) || - confirmationStrategies.HasFlag(TaskConfirmationStrategies.AdminSignsOffJustInTime); - public static bool UsesServicePrincipal(this TaskConfirmationStrategies confirmationStrategies) => - confirmationStrategies.HasFlag(TaskConfirmationStrategies.AutomaticRekeyingAsNeeded) || - confirmationStrategies.HasFlag(TaskConfirmationStrategies.AutomaticRekeyingScheduled) || - confirmationStrategies.HasFlag(TaskConfirmationStrategies.ExternalSignal); - } -} diff --git a/src/AuthJanitor.AspNet/TaskExecutionMetaService.cs b/src/AuthJanitor.AspNet/TaskExecutionMetaService.cs deleted file mode 100644 index 49552c5..0000000 --- a/src/AuthJanitor.AspNet/TaskExecutionMetaService.cs +++ /dev/null @@ -1,214 +0,0 @@ -// 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 _managedSecrets; - private readonly IDataStore _rekeyingTasks; - private readonly IDataStore _resources; - private readonly ISecureStorage _secureStorageProvider; - - private readonly ILogger _logger; - private readonly IServiceProvider _serviceProvider; - private readonly ProviderManagerService _providerManagerService; - - private readonly EventDispatcherService _eventDispatcherService; - private readonly IIdentityService _identityService; - - private readonly AuthJanitorService _authJanitorService; - - public TaskExecutionMetaService( - ILogger logger, - IServiceProvider serviceProvider, - EventDispatcherService eventDispatcherService, - IIdentityService identityService, - ProviderManagerService providerManagerService, - IDataStore managedSecrets, - IDataStore rekeyingTasks, - IDataStore 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 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(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 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); - } - } -} diff --git a/src/AuthJanitor.AspNet/TestAsContexts.cs b/src/AuthJanitor.AspNet/TestAsContexts.cs deleted file mode 100644 index 853d6ff..0000000 --- a/src/AuthJanitor.AspNet/TestAsContexts.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -namespace AuthJanitor.UI.Shared -{ - public enum TestAsContexts - { - Unknown, - AsUser, - AsApp - } -} diff --git a/src/AuthJanitor.AspNet/TimeSpanConverter.cs b/src/AuthJanitor.AspNet/TimeSpanConverter.cs deleted file mode 100644 index 6937b0a..0000000 --- a/src/AuthJanitor.AspNet/TimeSpanConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace AuthJanitor.UI.Shared -{ - internal sealed class TimeSpanConverter : JsonConverter - { - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return TimeSpan.Parse(reader.GetString()); - } - - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModelFactory.cs b/src/AuthJanitor.AspNet/ViewModelFactory.cs deleted file mode 100644 index 7d86db4..0000000 --- a/src/AuthJanitor.AspNet/ViewModelFactory.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using AuthJanitor.UI.Shared.ViewModels; -using AuthJanitor.Providers; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; -using AuthJanitor.IdentityServices; -using AuthJanitor.Providers.Capabilities; -using AuthJanitor.DataStores; - -namespace AuthJanitor.UI.Shared -{ - public static class ViewModelFactory - { - private static IDictionary InputTypes { get; } = new Dictionary() - { - { typeof(string), ProviderConfigurationItemViewModel.InputTypes.Text }, - { typeof(string[]), ProviderConfigurationItemViewModel.InputTypes.TextArray }, - { typeof(int), ProviderConfigurationItemViewModel.InputTypes.Integer }, - { typeof(bool), ProviderConfigurationItemViewModel.InputTypes.Boolean }, - { typeof(Enum), ProviderConfigurationItemViewModel.InputTypes.Enumeration } - }; - private static IDictionary> ValueReaders { get; } = new Dictionary>() - { - { typeof(string), (instance, property) => property.GetValue(instance) as string }, - { typeof(string[]), (instance, property) => property.GetValue(instance) == null ? string.Empty : string.Join(",", property.GetValue(instance) as string[]) }, - { typeof(int), (instance, property) => (property.GetValue(instance) as int?).GetValueOrDefault(0).ToString() }, - { typeof(bool), (instance, property) => (property.GetValue(instance) as bool?).GetValueOrDefault(false).ToString() }, - { typeof(Enum), (instance, property) => (property.GetValue(instance) as Enum).ToString() } - }; - - public static void ConfigureServices(IServiceCollection serviceCollection) - { - serviceCollection.AddTransient>(serviceProvider => provider => GetViewModel(serviceProvider, provider)); - serviceCollection.AddTransient>(serviceProvider => secret => GetViewModel(serviceProvider, secret, CancellationToken.None)); - serviceCollection.AddTransient>(serviceProvider => resource => GetViewModel(serviceProvider, resource)); - serviceCollection.AddTransient>(serviceProvider => rekeyingTask => GetViewModel(serviceProvider, rekeyingTask, CancellationToken.None)); - serviceCollection.AddTransient>(serviceProvider => scheduleWindow => GetViewModel(serviceProvider, scheduleWindow)); - serviceCollection.AddTransient>(serviceProvider => config => GetViewModel(serviceProvider, config)); - serviceCollection.AddTransient>(serviceProvider => authorizedUser => GetViewModel(serviceProvider, authorizedUser)); - } - -#pragma warning disable IDE0060 // Remove unused parameter - private static ProviderConfigurationViewModel GetViewModel(IServiceProvider serviceProvider, AuthJanitorProviderConfiguration config) => -#pragma warning restore IDE0060 // Remove unused parameter - new ProviderConfigurationViewModel() - { - ConfigurationItems = config.GetType().GetProperties() - .Select(property => - { - if (!InputTypes.Any(t => t.Key.IsAssignableFrom(property.PropertyType)) || - !ValueReaders.Any(v => v.Key.IsAssignableFrom(property.PropertyType))) - throw new NotImplementedException($"Provider Configuration includes Type '{property.PropertyType.Name}', which is not supported"); - - var inputType = InputTypes.First(t => t.Key.IsAssignableFrom(property.PropertyType)).Value; - var valueReader = ValueReaders.First(t => t.Key.IsAssignableFrom(property.PropertyType)).Value; - - return new ProviderConfigurationItemViewModel() - { - Name = property.Name, - DisplayName = property.GetCustomAttribute() == null ? - property.Name : - property.GetCustomAttribute().DisplayName, - HelpText = property.GetCustomAttribute() == null ? - string.Empty : - property.GetCustomAttribute().Description, - InputType = inputType, - Options = inputType == ProviderConfigurationItemViewModel.InputTypes.Enumeration ? - property.PropertyType.GetEnumValues().Cast() - .ToDictionary( - k => k.ToString(), - v => v.GetEnumValueAttribute() == null ? - v.ToString() : - v.GetEnumValueAttribute().Description) - .Select(i => new ProviderConfigurationItemViewModel.SelectOption(i.Key, i.Value)) : - new List(), - Value = valueReader(config, property) - }; - }) - }; - -#pragma warning disable IDE0060 // Remove unused parameter - private static LoadedProviderViewModel GetViewModel(IServiceProvider serviceProvider, LoadedProviderMetadata provider) => -#pragma warning restore IDE0060 // Remove unused parameter - new LoadedProviderViewModel() - { - AssemblyVersion = provider.AssemblyName.Version.ToString(), - Details = provider.Details, - OriginatingFile = Path.GetFileName(provider.OriginatingFile), - ProviderTypeName = provider.ProviderTypeName, - Capabilities = GetProviderCapabilities(provider.ProviderType) - }; - - private static IEnumerable GetProviderCapabilities(Type providerType) - { - var capabilities = new List(); - if (typeof(ICanEnumerateResourceCandidates).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanEnumerateResourceCandidates); - if (typeof(ICanRunSanityTests).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanRunSanityTests); - if (typeof(ICanCleanup).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanCleanup); - if (typeof(ICanDistributeTemporarySecretValues).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanDistributeTemporarySecrets); - if (typeof(ICanGenerateTemporarySecretValue).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanGenerateTemporarySecrets); - if (typeof(ICanPerformUnifiedCommit).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanPerformUnifiedCommits); - if (typeof(ICanPerformUnifiedCommitForTemporarySecretValues).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanPerformUnifiedCommitForTemporarySecret); - if (typeof(ICanCleanup).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanCleanup); - if (typeof(ICanRekey).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanRekey); - if (typeof(ICanDistributeLongTermSecretValues).IsAssignableFrom(providerType)) - capabilities.Add(ProviderCapabilities.CanDistributeLongTermSecrets); - return capabilities; - } - - private static ManagedSecretViewModel GetViewModel(IServiceProvider serviceProvider, ManagedSecret secret, CancellationToken cancellationToken) - { - var providerManagerService = serviceProvider.GetRequiredService(); - var resources = secret.ResourceIds - .Select(resourceId => serviceProvider.GetRequiredService>() - .GetOne(resourceId, cancellationToken).Result) - .Select(resource => serviceProvider.GetRequiredService>()(resource)); - foreach (var resource in resources) - { - var provider = providerManagerService.GetProviderInstance(resource.ProviderType, resource.SerializedProviderConfiguration); - resource.Risks = provider.GetRisks(secret.ValidPeriod); - resource.Description = provider.GetDescription(); - } - return new ManagedSecretViewModel() - { - ObjectId = secret.ObjectId, - Name = secret.Name, - Description = secret.Description, - TaskConfirmationStrategies = secret.TaskConfirmationStrategies, - ExecutingAgentId = secret.ExecutingAgentId, - LastChanged = secret.LastChanged, - ValidPeriodMinutes = (int)secret.ValidPeriod.TotalMinutes, - Nonce = secret.Nonce, - Resources = resources, - AdminEmails = secret.AdminEmails - }; - } - - private static RekeyingTaskViewModel GetViewModel(IServiceProvider serviceProvider, RekeyingTask rekeyingTask, CancellationToken cancellationToken) - { - ManagedSecretViewModel secret; - try - { - secret = serviceProvider.GetRequiredService>()( - serviceProvider.GetRequiredService>().GetOne(rekeyingTask.ManagedSecretId, cancellationToken).Result); - } - catch (Exception) { secret = new ManagedSecretViewModel() { ObjectId = Guid.Empty }; } - string errorMessage = string.Empty; - var mostRecentAttempt = rekeyingTask?.Attempts.OrderByDescending(a => a.StartedExecution).FirstOrDefault(); - - if (mostRecentAttempt != null) - errorMessage = mostRecentAttempt.HasBeenExecutedSuccessfully ? - string.Empty : mostRecentAttempt.GetLastException(); - - return new RekeyingTaskViewModel() - { - ObjectId = rekeyingTask.ObjectId, - Queued = rekeyingTask.Queued, - Expiry = rekeyingTask.Expiry, - PersistedCredentialUser = rekeyingTask.PersistedCredentialUser, - ConfirmationType = rekeyingTask.ConfirmationType, - RekeyingCompleted = rekeyingTask.RekeyingCompleted, - RekeyingErrorMessage = errorMessage, - RekeyingInProgress = rekeyingTask.RekeyingInProgress, - ManagedSecret = secret, - Attempts = rekeyingTask.Attempts - }; - } - - private static ResourceViewModel GetViewModel(IServiceProvider serviceProvider, Resource resource) - { - var providerManagerService = serviceProvider.GetRequiredService(); - var provider = providerManagerService.GetProviderInstance(resource.ProviderType, resource.ProviderConfiguration); - - return new ResourceViewModel() - { - ObjectId = resource.ObjectId, - Name = resource.Name, - Description = resource.Description, - ProviderType = resource.ProviderType, - Provider = GetViewModel(serviceProvider, providerManagerService.GetProviderMetadata(resource.ProviderType)), - SerializedProviderConfiguration = resource.ProviderConfiguration, - RuntimeDescription = provider.GetDescription(), - Risks = provider.GetRisks() - }; - } - -#pragma warning disable IDE0060 // Remove unused parameter - private static ScheduleWindowViewModel GetViewModel(IServiceProvider serviceProvider, ScheduleWindow scheduleWindow) => -#pragma warning restore IDE0060 // Remove unused parameter - new ScheduleWindowViewModel() - { - ObjectId = scheduleWindow.ObjectId, - CronStrings = new List(scheduleWindow.CronStrings) - }; - - private static AuthJanitorAuthorizedUserViewModel GetViewModel(IServiceProvider serviceProvider, AuthJanitorAuthorizedUser authorizedUser) => - new AuthJanitorAuthorizedUserViewModel() - { - UPN = authorizedUser.UPN, - DisplayName = authorizedUser.DisplayName, - RoleValue = authorizedUser.RoleValue - }; - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/AuthJanitorAuthorizedUserViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/AuthJanitorAuthorizedUserViewModel.cs deleted file mode 100644 index 7d26aef..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/AuthJanitorAuthorizedUserViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class AuthJanitorAuthorizedUserViewModel : IAuthJanitorViewModel - { - public string DisplayName { get; set; } - public string UPN { get; set; } - public string RoleValue { get; set; } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/DashboardMetricsViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/DashboardMetricsViewModel.cs deleted file mode 100644 index c2e781b..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/DashboardMetricsViewModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System.Collections.Generic; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class DashboardMetricsViewModel : IAuthJanitorViewModel - { - public string SignedInName { get; set; } = "John Doe"; - public string SignedInEmail { get; set; } = "john.doe@contoso.com"; - public string SignedInRoles { get; set; } = "NoRole"; - - public int TotalSecrets { get; set; } - public int TotalResources { get; set; } - public int TotalExpiringSoon { get; set; } - public int TotalExpired { get; set; } - public int TasksInError { get; set; } - - public int TotalPendingApproval { get; set; } - public int PercentExpired { get; set; } - - public int Risk0 { get; set; } - public int Risk35 { get; set; } - public int Risk60 { get; set; } - public int Risk85 { get; set; } - public int RiskOver85 { get; set; } - - public IEnumerable ExpiringSoon { get; set; } = new List(); - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/IAuthJanitorViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/IAuthJanitorViewModel.cs deleted file mode 100644 index 3bbec08..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/IAuthJanitorViewModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -namespace AuthJanitor.UI.Shared.ViewModels -{ - public interface IAuthJanitorViewModel - { - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/LoadedProviderViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/LoadedProviderViewModel.cs deleted file mode 100644 index 8349c12..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/LoadedProviderViewModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System.Collections.Generic; -using AuthJanitor.Providers; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public enum ProviderCapabilities - { - None, - CanCleanup, - CanDistributeLongTermSecrets, - CanDistributeTemporarySecrets, - CanEnumerateResourceCandidates, - CanGenerateTemporarySecrets, - CanPerformUnifiedCommits, - CanPerformUnifiedCommitForTemporarySecret, - CanRekey, - CanRunSanityTests - } - - public class LoadedProviderViewModel : IAuthJanitorViewModel - { - public string OriginatingFile { get; set; } - public string ProviderTypeName { get; set; } - public ProviderAttribute Details { get; set; } - public IEnumerable Capabilities { get; set; } = new[] { ProviderCapabilities.None }; - public string AssemblyVersion { get; set; } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/ManagedSecretViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/ManagedSecretViewModel.cs deleted file mode 100644 index c17f232..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/ManagedSecretViewModel.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class ManagedSecretViewModel : IAuthJanitorViewModel - { - public Guid ObjectId { get; set; } - public string Name { get; set; } - public string Description { get; set; } - - [JsonIgnore] - public TaskConfirmationStrategies TaskConfirmationStrategies - { - get => (TaskConfirmationStrategies)TaskConfirmationStrategiesInt; - set => TaskConfirmationStrategiesInt = (int)value; - } - - public int TaskConfirmationStrategiesInt { get; set; } - public string ExecutingAgentId { get; set; } - - public DateTimeOffset? LastChanged { get; set; } - public int ValidPeriodMinutes { get; set; } - - [JsonIgnore] - public TimeSpan ValidPeriod => TimeSpan.FromMinutes(ValidPeriodMinutes); - - public IEnumerable AdminEmails { get; set; } = new List(); - - /// - /// If the ManagedSecret is valid - /// - [JsonIgnore] - public bool IsValid => Expiry > DateTimeOffset.UtcNow; - - /// - /// Date/Time of expiry - /// - [JsonIgnore] - public DateTimeOffset Expiry => LastChanged.GetValueOrDefault() + ValidPeriod; - - /// - /// Time remaining until Expiry (if expired, TimeSpan.Zero) - /// - [JsonIgnore] - public TimeSpan TimeRemaining => IsValid ? Expiry - DateTimeOffset.UtcNow : TimeSpan.Zero; - - [JsonIgnore] - public string ProviderSummary => $"{Resources.Count(r => !r.Provider.Capabilities.Contains(ProviderCapabilities.CanDistributeLongTermSecrets))} ALCs, " + - $"{Resources.Count(r => r.Provider.Capabilities.Contains(ProviderCapabilities.CanRekey))} RKOs"; - - [JsonIgnore] - public int ExpiryPercent => IsValid ? - (int)Math.Round(((ValidPeriod - TimeRemaining) / ValidPeriod) * 100, 0) : - 100; - - public string Nonce { get; set; } - - public string ResourceIds { get; set; } = string.Empty; - public IEnumerable Resources { get; set; } = new List(); - - public ManagedSecretViewModel() - { - ObjectId = Guid.Empty; - Name = "New Managed Secret"; - Description = "Manages a secret between a rekeyable resource and application."; - TaskConfirmationStrategies = TaskConfirmationStrategies.None; - ValidPeriodMinutes = 60 * 24 * 90; // 90 days - Nonce = string.Empty; - } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/ProviderConfigurationItemViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/ProviderConfigurationItemViewModel.cs deleted file mode 100644 index 8e5fae0..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/ProviderConfigurationItemViewModel.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class ProviderConfigurationItemViewModel : IAuthJanitorViewModel - { - public enum InputTypes - { - Text, - TextArray, - Integer, - Boolean, - Enumeration - } - - public string Name { get; set; } - public string DisplayName { get; set; } - public InputTypes InputType { get; set; } - public string HelpText { get; set; } - public string Value { get; set; } - public IEnumerable Options { get; set; } = new List(); - - [JsonIgnore] - public bool BoolValue - { - get { bool.TryParse(Value, out bool boolValue); return boolValue; } - set => Value = value.ToString(); - } - - [JsonIgnore] - public int IntValue - { - get { int.TryParse(Value, out int intValue); return intValue; } - set => Value = value.ToString(); - } - - public class SelectOption - { - public string Value { get; set; } - public string DisplayName { get; set; } - - public SelectOption() { } - - public SelectOption(string value, string displayName) - { - Value = value; - DisplayName = displayName; - } - } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/ProviderConfigurationViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/ProviderConfigurationViewModel.cs deleted file mode 100644 index a6fafde..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/ProviderConfigurationViewModel.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class ProviderConfigurationViewModel : IAuthJanitorViewModel - { - public IEnumerable ConfigurationItems { get; set; } = new List(); - - [JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public string SerializedConfiguration - { - get - { - var dict = new Dictionary(); - foreach (var item in ConfigurationItems) - { - switch (item.InputType) - { - case ProviderConfigurationItemViewModel.InputTypes.Boolean: - dict.Add(item.Name, item.BoolValue); - break; - case ProviderConfigurationItemViewModel.InputTypes.Enumeration: - dict.Add(item.Name, item.IntValue); - break; - case ProviderConfigurationItemViewModel.InputTypes.Integer: - dict.Add(item.Name, item.IntValue); - break; - case ProviderConfigurationItemViewModel.InputTypes.Text: - dict.Add(item.Name, item.Value); - break; - case ProviderConfigurationItemViewModel.InputTypes.TextArray: - dict.Add(item.Name, item.Value); // TODO: Check this - break; - } - } - return JsonSerializer.Serialize(dict); - } - set - { - var deserialized = JsonSerializer.Deserialize>(value); - foreach (var item in ConfigurationItems) - { - if (deserialized.ContainsKey(item.Name)) - { - switch (item.InputType) - { - case ProviderConfigurationItemViewModel.InputTypes.Text: - item.Value = deserialized[item.Name].GetString(); - break; - case ProviderConfigurationItemViewModel.InputTypes.TextArray: - // TODO: Fix this - item.Value = deserialized[item.Name].GetString(); - break; - case ProviderConfigurationItemViewModel.InputTypes.Integer: - item.IntValue = deserialized[item.Name].GetInt32(); - break; - case ProviderConfigurationItemViewModel.InputTypes.Boolean: - item.BoolValue = deserialized[item.Name].GetBoolean(); - break; - case ProviderConfigurationItemViewModel.InputTypes.Enumeration: - item.IntValue = deserialized[item.Name].GetInt32(); - break; - } - } - } - } - } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/ProviderResourceSuggestionViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/ProviderResourceSuggestionViewModel.cs deleted file mode 100644 index 379c0cf..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/ProviderResourceSuggestionViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.ViewModels; -using System.Collections.Generic; - -namespace AuthJanitor.ViewModels -{ - public class ProviderResourceSuggestionViewModel : IAuthJanitorViewModel - { - public string Name { get; set; } = string.Empty; - public string ProviderType { get; set; } = string.Empty; - public string ProviderConfigurationSerialized { get; set; } = string.Empty; - public ProviderConfigurationViewModel ProviderConfiguration { get; set; } - public IEnumerable ResourcesAddressingThis { get; set; } - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/RekeyingTaskViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/RekeyingTaskViewModel.cs deleted file mode 100644 index f3f599e..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/RekeyingTaskViewModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Providers; -using System; -using System.Collections.Generic; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class RekeyingTaskViewModel : IAuthJanitorViewModel - { - public Guid ObjectId { get; set; } - - public DateTimeOffset Queued { get; set; } - public DateTimeOffset Expiry { get; set; } - - public bool RekeyingInProgress { get; set; } = false; - public bool RekeyingCompleted { get; set; } = false; - public string RekeyingErrorMessage { get; set; } = string.Empty; - - public TaskConfirmationStrategies ConfirmationType { get; set; } - - public string PersistedCredentialUser { get; set; } - - public Guid AvailabilityScheduleId { get; set; } - - public ManagedSecretViewModel ManagedSecret { get; set; } = new ManagedSecretViewModel(); - - public List Attempts { get; set; } = new List(); - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/ResourceViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/ResourceViewModel.cs deleted file mode 100644 index be6e9e6..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/ResourceViewModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Providers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json.Serialization; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class ResourceViewModel : IAuthJanitorViewModel - { - public Guid ObjectId { get; set; } - public string Name { get; set; } - public string Description { get; set; } - - public string ProviderType { get; set; } - public LoadedProviderViewModel Provider { get; set; } - public string SerializedProviderConfiguration { get; set; } - public IEnumerable Risks { get; set; } = new List(); - public string RuntimeDescription { get; set; } - public int RiskScore => Risks.Sum(r => r.Score); - - [JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public ProviderConfigurationViewModel ProviderConfiguration { get; set; } = new ProviderConfigurationViewModel(); - } -} diff --git a/src/AuthJanitor.AspNet/ViewModels/ScheduleWindowViewModel.cs b/src/AuthJanitor.AspNet/ViewModels/ScheduleWindowViewModel.cs deleted file mode 100644 index d4a4cd8..0000000 --- a/src/AuthJanitor.AspNet/ViewModels/ScheduleWindowViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; -using System.Collections.Generic; -//using System.Linq; - -namespace AuthJanitor.UI.Shared.ViewModels -{ - public class ScheduleWindowViewModel : IAuthJanitorViewModel - { - public Guid ObjectId { get; set; } - - public IEnumerable CronStrings { get; set; } = new List(); - - //[JsonIgnore] - //public DateTimeOffset NextOccurrence => - // CronStrings.Select(s => CronExpression.Parse(s)) - // .Select(c => c.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Utc).GetValueOrDefault()) - // .OrderBy(c => c.Ticks) - // .First(); - } -} diff --git a/src/AuthJanitor.Automation.Blazor/.config/dotnet-tools.json b/src/AuthJanitor.Automation.Blazor/.config/dotnet-tools.json new file mode 100644 index 0000000..96442e7 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "5.0.1", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/App.razor b/src/AuthJanitor.Automation.Blazor/App.razor new file mode 100644 index 0000000..476d65e --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/App.razor @@ -0,0 +1,71 @@ + + + + + + + +
+

(╯°□°)╯︵ ┻━┻

+

You've landed on a broken link.

+

+ If you think this message is the result of a software bug, please + open a new issue + with the AuthJanitor GitHub repository. +

+
+
+
+
+
+@code{ + private Theme theme = new Theme + { + BarOptions = new ThemeBarOptions + { + HorizontalHeight = "64px" + }, + BreakpointOptions = new ThemeBreakpointOptions + { + }, + ColorOptions = new ThemeColorOptions + { + Primary = "#0288D1", + //Secondary = "#A65529", + //Success = "#23C02E", + //Info = "#9BD8FE", + //Warning = "#F8B86C", + //Danger = "#F95741", + //Light = "#F0F0F0", + //Dark = "#535353", + }, + BackgroundOptions = new ThemeBackgroundOptions + { + Primary = "#0288D1", + }, + TextColorOptions = new ThemeTextColorOptions + { + }, + //ButtonOptions = new ThemeButtonOptions { }, + //DropdownOptions = new ThemeDropdownOptions { }, + InputOptions = new ThemeInputOptions + { + CheckColor = "#0288D1", + }, + //CardOptions = new ThemeCardOptions { }, + //ModalOptions = new ThemeModalOptions { }, + //TabsOptions = new ThemeTabsOptions { }, + //ProgressOptions = new ThemeProgressOptions { }, + //AlertOptions = new ThemeAlertOptions { }, + //BreadcrumbOptions = new ThemeBreadcrumbOptions { }, + //BadgeOptions = new ThemeBadgeOptions { }, + //PaginationOptions = new ThemePaginationOptions { }, + //TooltipOptions = new ThemeTooltipOptions + //{ + // BackgroundColor = "#22c8ce", + // BackgroundOpacity = .7f, + // Color = "#ff0000", + //}, + }; +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/AuthJanitor.Automation.Blazor.csproj b/src/AuthJanitor.Automation.Blazor/AuthJanitor.Automation.Blazor.csproj new file mode 100644 index 0000000..14abd8d --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/AuthJanitor.Automation.Blazor.csproj @@ -0,0 +1,54 @@ + + + + netcoreapp3.1 + e1722313-e052-415a-848c-8afec77445d8 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/src/AuthJanitor.Automation.Blazor/BlazorTokenCredentialProvider.cs b/src/AuthJanitor.Automation.Blazor/BlazorTokenCredentialProvider.cs new file mode 100644 index 0000000..6053ed1 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/BlazorTokenCredentialProvider.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using AuthJanitor.SecureStorage; +using Microsoft.Identity.Web; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor +{ + public class BlazorTokenCredentialProvider : ITokenCredentialProvider + { + public const string AZURE_SCOPE = "https://management.core.windows.net/user_impersonation"; + private readonly ISecureStorage _secureStorage; + private readonly ITokenAcquisition _tokenAcquisition; + + public BlazorTokenCredentialProvider( + ISecureStorage secureStorage, + ITokenAcquisition tokenAcquisition) + { + _secureStorage = secureStorage; + _tokenAcquisition = tokenAcquisition; + } + + public async Task GetToken(TokenSources source, string parameters) + { + switch (source) + { + case TokenSources.Explicit: + return AccessTokenCredential.CreateBearer(parameters); + case TokenSources.Persisted: + var guid = Guid.Parse(parameters); + return await _secureStorage.Retrieve(guid); + case TokenSources.ServicePrincipal: + return AccessTokenCredential.CreateBearer( + await _tokenAcquisition.GetAccessTokenForAppAsync(AZURE_SCOPE)); + case TokenSources.OBO: + return AccessTokenCredential.CreateBearer( + await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { AZURE_SCOPE })); + } + return null; + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Components/LoadedProviderMetadataComponent.razor b/src/AuthJanitor.Automation.Blazor/Components/LoadedProviderMetadataComponent.razor new file mode 100644 index 0000000..02e354e --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Components/LoadedProviderMetadataComponent.razor @@ -0,0 +1,45 @@ + + + + @((MarkupString)Provider.Details.SvgImage) + + + + + + @Provider.Details.Name + + + + + + + + + + + @Provider.Details.Description + + + + + + + +@using AuthJanitor.Providers +@code +{ + [Parameter] + public LoadedProviderMetadata Provider { get; set; } + + [Parameter] + public string ProviderType { get; set; } + + protected override void OnInitialized() + { + if (Provider == null && !string.IsNullOrEmpty(ProviderType)) + { + Provider = AuthJanitorService.ProviderManager.GetProviderMetadata(ProviderType); + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Components/ProviderExecutionParametersComponent.razor b/src/AuthJanitor.Automation.Blazor/Components/ProviderExecutionParametersComponent.razor new file mode 100644 index 0000000..756e6df --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Components/ProviderExecutionParametersComponent.razor @@ -0,0 +1,38 @@ + + + + + + + + + + + + Token Source + + + + @Parameters.TokenSource + @Parameters.TokenParameter + + + + + + Agent + + + + @Parameters.AgentId + + + + + + +@using AuthJanitor.Providers +@code { + [Parameter] + public ProviderExecutionParameters Parameters { get; set; } +} diff --git a/src/AuthJanitor.Automation.Blazor/Components/ThemeColorSelector.razor b/src/AuthJanitor.Automation.Blazor/Components/ThemeColorSelector.razor new file mode 100644 index 0000000..de25237 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Components/ThemeColorSelector.razor @@ -0,0 +1,66 @@ + +
+ @foreach (var color in ThemeColors.Items.Values) + { +
+ @foreach (var shade in color.Shades.Values) + { + var temp = shade.Value; + +
+ } +
+ } +
+@code{ + [Parameter] + public string Value + { + get => _value; + set + { + if (value == _value) + { + return; + } + _value = value; + this.StateHasChanged(); + ValueChanged.InvokeAsync(value); + } + } + + string ClassNames(string value) + => $"demo-theme-color-item{(value == Value ? " selected" : "")}"; + + [Parameter] + public EventCallback ValueChanged { get; set; } + + private string _value; + + Task OnClick(string value) + { + Value = value; + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Connected Services/AzureAD/ConnectedService.json b/src/AuthJanitor.Automation.Blazor/Connected Services/AzureAD/ConnectedService.json new file mode 100644 index 0000000..41463fd --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Connected Services/AzureAD/ConnectedService.json @@ -0,0 +1,10 @@ +{ + "ProviderId": "Microsoft.Azure.AD", + "Version": "16.8", + "GettingStartedDocument": { + "Uri": "https://go.microsoft.com/fwlink/?linkid=870469" + }, + "ExtendedData": { + "AuthConfiguration": "AspNetCore" + } +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadOnlyEntityControllerBase.cs b/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadOnlyEntityControllerBase.cs new file mode 100644 index 0000000..64aeb8e --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadOnlyEntityControllerBase.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Repository; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + public abstract class ReadOnlyEntityControllerBase : ControllerBase + where TEntity : class + { + protected readonly AuthJanitorDbContext _context; + + public ReadOnlyEntityControllerBase(AuthJanitorDbContext context) + { + _context = context; + } + + protected abstract DbSet DbSet { get; } + protected abstract Func GetId { get; } + + [HttpGet] + public virtual async Task>> Get() => + await DbSet.ToListAsync(); + + [HttpGet("{id}")] + public virtual async Task> Get(Guid id) + { + var model = await DbSet.FindAsync(id); + if (model == null) + return NotFound(); + else return model; + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadWriteEntityControllerBase.cs b/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadWriteEntityControllerBase.cs new file mode 100644 index 0000000..975efa6 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadWriteEntityControllerBase.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Repository; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + public abstract class ReadWriteEntityControllerBase : + ReadOnlyEntityControllerBase + where TEntity : class + { + public ReadWriteEntityControllerBase(AuthJanitorDbContext context) + : base(context) { } + + [HttpPost] + public virtual async Task> Post(TEntity model) + { + DbSet.Add(model); + await _context.SaveChangesAsync(); + + return CreatedAtAction("Post", new { id = GetId(model) }, model); + } + + [HttpDelete("{id}")] + public virtual async Task> Delete(Guid id) + { + var model = await DbSet.FindAsync(id); + if (model == null) + return NotFound(); + + DbSet.Remove(model); + await _context.SaveChangesAsync(); + + return model; + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadWriteUpdateEntityControllerBase.cs b/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadWriteUpdateEntityControllerBase.cs new file mode 100644 index 0000000..8f682c4 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/Common/ReadWriteUpdateEntityControllerBase.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Repository; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + public abstract class ReadWriteUpdateEntityControllerBase : + ReadWriteEntityControllerBase + where TEntity : class + { + public ReadWriteUpdateEntityControllerBase(AuthJanitorDbContext context) + : base(context) { } + + [HttpPut("{id}")] + public virtual async Task Put(Guid id, TEntity model) + { + if (id != GetId(model)) + return BadRequest(); + + _context.Entry(model).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await DbSet.AnyAsync(r => GetId(r) == id)) + return NotFound(); + throw; + } + + return NoContent(); + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/DependencyGroupController.cs b/src/AuthJanitor.Automation.Blazor/Controllers/DependencyGroupController.cs new file mode 100644 index 0000000..11c4608 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/DependencyGroupController.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Repository; +using AuthJanitor.Repository.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + [Route("api/dependencygroups")] + [ApiController] + public class DependencyGroupController : ReadWriteUpdateEntityControllerBase + { + public DependencyGroupController(AuthJanitorDbContext context) : base(context) + { + } + + protected override DbSet DbSet => _context.DependencyGroups; + + protected override Func GetId => + new Func((r) => r.DependencyGroupId); + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/DependencyGroupRotationController.cs b/src/AuthJanitor.Automation.Blazor/Controllers/DependencyGroupRotationController.cs new file mode 100644 index 0000000..67e7aa0 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/DependencyGroupRotationController.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Repository; +using AuthJanitor.Repository.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + [Route("api/rotations")] + [ApiController] + public class DependencyGroupRotationController : ReadOnlyEntityControllerBase + { + public DependencyGroupRotationController(AuthJanitorDbContext context) : base(context) + { + } + + protected override DbSet DbSet => _context.DependencyGroups; + + protected override Func GetId => + new Func((r) => r.DependencyGroupId); + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/ResourceController.cs b/src/AuthJanitor.Automation.Blazor/Controllers/ResourceController.cs new file mode 100644 index 0000000..cf7a500 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/ResourceController.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using AuthJanitor.Repository; +using AuthJanitor.Repository.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Identity.Web; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + [Route("api/resources")] + [ApiController] + public class ResourceController : ReadWriteEntityControllerBase + { + private readonly AuthJanitorService _authJanitorService; + private readonly ITokenAcquisition _tokens; + + public ResourceController( + AuthJanitorDbContext context, + AuthJanitorService authJanitorService, + ITokenAcquisition tokenAbstraction) : base(context) + { + _authJanitorService = authJanitorService; + _tokens = tokenAbstraction; + } + + protected override DbSet DbSet => _context.Resources; + + protected override Func GetId => + new Func((r) => r.ResourceId); + + [HttpGet("enumerate")] + public async Task>> Enumerate() + { + var token = AccessTokenCredential.CreateBearer(await _tokens.GetAccessTokenForUserAsync(new[] { + BlazorTokenCredentialProvider.AZURE_SCOPE })); + var resources = await _authJanitorService.EnumerateAsync(token); + return Ok( + JsonConvert.SerializeObject(resources, + new JsonSerializerSettings() + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + })); + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Controllers/SystemController.cs b/src/AuthJanitor.Automation.Blazor/Controllers/SystemController.cs new file mode 100644 index 0000000..81084de --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Controllers/SystemController.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using AuthJanitor.Repository; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; +using System.Linq; + +namespace AuthJanitor.Automation.Blazor.Controllers +{ + [Route("api/system")] + [ApiController] + public class SystemController : ControllerBase + { + private readonly AuthJanitorDbContext _context; + private readonly AuthJanitorService _authJanitorService; + + public SystemController( + AuthJanitorDbContext context, + AuthJanitorService authJanitorService) + { + _context = context; + _authJanitorService = authJanitorService; + } + + [HttpGet("providers")] + public ActionResult> EnumerateLoadedProviders() + { + return Ok(_authJanitorService.LoadedProviders + .Select(p => new LoadedProviderMetadata() + { + ProviderTypeName = p.ProviderTypeName, + OriginatingFile = p.OriginatingFile, + Details = p.Details + })); + } + + [HttpGet("providers/{providerType}")] + public ActionResult CreateBlankConfiguration( + string providerType) + { + return Ok(_authJanitorService + .ProviderManager + .GetProviderConfiguration(providerType)); + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Pages/DependencyGroups.razor b/src/AuthJanitor.Automation.Blazor/Pages/DependencyGroups.razor new file mode 100644 index 0000000..adbef5f --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Pages/DependencyGroups.razor @@ -0,0 +1,45 @@ +@page "/dependencygroups" + +@* rowselectable is false here! *@ + + + + + + +
    + @foreach (var item in context.Resources) + { +
  • + @item.Resource.DescriptiveName +
  • + } +
+
+
+
+ +@inherits EntityBasePage +@using AuthJanitor.Repository.Models +@code +{ + protected override string UrlBase => "/api/dependencygroups"; + protected override Func GetId => new Func( + (rm) => rm.DependencyGroupId); +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Pages/Error.cshtml b/src/AuthJanitor.Automation.Blazor/Pages/Error.cshtml new file mode 100644 index 0000000..6436437 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Pages/Error.cshtml @@ -0,0 +1,16 @@ +@page + + +

Error.

+

An error occurred while processing your request.

+ +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

diff --git a/src/AuthJanitor.Automation.Blazor/Pages/Index.razor b/src/AuthJanitor.Automation.Blazor/Pages/Index.razor new file mode 100644 index 0000000..956d859 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Pages/Index.razor @@ -0,0 +1,17 @@ +@page "/" + +

Hello, world!

+ +Welcome to your new app. + + + + @foreach (var item in AuthJanitorService.ProviderManager.LoadedProviders) + { + + + + + + } + \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Pages/Resources.razor b/src/AuthJanitor.Automation.Blazor/Pages/Resources.razor new file mode 100644 index 0000000..cf303e0 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Pages/Resources.razor @@ -0,0 +1,47 @@ +@page "/resources" + + + +@* rowselectable is false here! *@ + + + + + + @context.Parameters.ProviderType + + + + + @context.Parameters.ProviderConfiguration + + + + +@inherits EntityBasePage +@using AuthJanitor.Repository.Models +@code +{ + protected override string UrlBase => "/api/resources"; + protected override Func GetId => new Func( + (rm) => rm.ResourceId); +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Pages/_Host.cshtml b/src/AuthJanitor.Automation.Blazor/Pages/_Host.cshtml new file mode 100644 index 0000000..39b91c0 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Pages/_Host.cshtml @@ -0,0 +1,47 @@ +@page "/" +@namespace AuthJanitor.Automation.Blazor.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@{ + Layout = null; +} + + + + + + + AuthJanitor + + + + + + + + + + + + + + +
+ + An error has occurred. This application may no longer respond until reloaded. + + + An unhandled exception has occurred. See browser dev tools for details. + + Reload + 🗙 +
+ + + + + + + + + + diff --git a/src/AuthJanitor.Automation.Blazor/Program.cs b/src/AuthJanitor.Automation.Blazor/Program.cs new file mode 100644 index 0000000..39d045c --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Program.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace AuthJanitor.Automation.Blazor +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Properties/PublishProfiles/authjanitor - Web Deploy.pubxml b/src/AuthJanitor.Automation.Blazor/Properties/PublishProfiles/authjanitor - Web Deploy.pubxml new file mode 100644 index 0000000..6c738bc --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Properties/PublishProfiles/authjanitor - Web Deploy.pubxml @@ -0,0 +1,42 @@ + + + + + MSDeploy + /subscriptions/67c708de-facd-4332-a6da-c35b24f8db3a/resourceGroups/authjanitor-2021/providers/Microsoft.Web/sites/authjanitor + authjanitor-2021 + AzureWebSite + Debug + Any CPU + https://authjanitor.azurewebsites.net + True + False + ef1c9f16-af6b-44dd-9d83-dc0e5bd21b9e + authjanitor.scm.azurewebsites.net:443 + authjanitor + + True + WMSVC + True + $authjanitor + <_SavePWD>True + <_DestinationType>AzureWebSite + netcoreapp3.1 + false + true + broadscope.work + https://broadscope.work/WebApp-authjanitor.azurewebsites.net + True + true + 77d95ace-e1cc-4e87-ace1-d6871319e09e + bT8moMsW6VoN/atOmWDG0LwKJfqW+yS79wOP9NKPckw= + https://authjanitor.azurewebsites.net/ + + 185bb12a-cb44-4d85-81fe-297ba9e6b4d1 + https://authjanitor.azurewebsites.net/ + False + + \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Properties/ServiceDependencies/authjanitor - Web Deploy/profile.arm.json b/src/AuthJanitor.Automation.Blazor/Properties/ServiceDependencies/authjanitor - Web Deploy/profile.arm.json new file mode 100644 index 0000000..d722e65 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Properties/ServiceDependencies/authjanitor - Web Deploy/profile.arm.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_dependencyType": "appService.windows" + }, + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "authjanitor-2021", + "metadata": { + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "southcentralus", + "metadata": { + "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support." + } + }, + "resourceName": { + "type": "string", + "defaultValue": "authjanitor", + "metadata": { + "description": "Name of the main resource to be created by this template." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "variables": { + "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", + "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "location": "[parameters('resourceLocation')]", + "name": "[parameters('resourceName')]", + "type": "Microsoft.Web/sites", + "apiVersion": "2015-08-01", + "tags": { + "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty" + }, + "dependsOn": [ + "[variables('appServicePlan_ResourceId')]" + ], + "kind": "app", + "properties": { + "name": "[parameters('resourceName')]", + "kind": "app", + "httpsOnly": true, + "reserved": false, + "serverFarmId": "[variables('appServicePlan_ResourceId')]", + "siteConfig": { + "metadata": [ + { + "name": "CURRENT_STACK", + "value": "dotnetcore" + } + ] + } + }, + "identity": { + "type": "SystemAssigned" + } + }, + { + "location": "[parameters('resourceLocation')]", + "name": "[variables('appServicePlan_name')]", + "type": "Microsoft.Web/serverFarms", + "apiVersion": "2015-08-01", + "sku": { + "name": "S1", + "tier": "Standard", + "family": "S", + "size": "S1" + }, + "properties": { + "name": "[variables('appServicePlan_name')]" + } + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Properties/ServiceDependencies/authjanitor-admin-2021 - Web Deploy/profile.arm.json b/src/AuthJanitor.Automation.Blazor/Properties/ServiceDependencies/authjanitor-admin-2021 - Web Deploy/profile.arm.json new file mode 100644 index 0000000..111aa14 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Properties/ServiceDependencies/authjanitor-admin-2021 - Web Deploy/profile.arm.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_dependencyType": "appService.windows" + }, + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "anthturn-aj2021", + "metadata": { + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "southcentralus", + "metadata": { + "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support." + } + }, + "resourceName": { + "type": "string", + "defaultValue": "authjanitor-admin-2021", + "metadata": { + "description": "Name of the main resource to be created by this template." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "variables": { + "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", + "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "location": "[parameters('resourceLocation')]", + "name": "[parameters('resourceName')]", + "type": "Microsoft.Web/sites", + "apiVersion": "2015-08-01", + "tags": { + "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty" + }, + "dependsOn": [ + "[variables('appServicePlan_ResourceId')]" + ], + "kind": "app", + "properties": { + "name": "[parameters('resourceName')]", + "kind": "app", + "httpsOnly": true, + "reserved": false, + "serverFarmId": "[variables('appServicePlan_ResourceId')]", + "siteConfig": { + "metadata": [ + { + "name": "CURRENT_STACK", + "value": "dotnetcore" + } + ] + } + }, + "identity": { + "type": "SystemAssigned" + } + }, + { + "location": "[parameters('resourceLocation')]", + "name": "[variables('appServicePlan_name')]", + "type": "Microsoft.Web/serverFarms", + "apiVersion": "2015-08-01", + "sku": { + "name": "S1", + "tier": "Standard", + "family": "S", + "size": "S1" + }, + "properties": { + "name": "[variables('appServicePlan_name')]" + } + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Properties/launchSettings.json b/src/AuthJanitor.Automation.Blazor/Properties/launchSettings.json new file mode 100644 index 0000000..b0f2984 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "AuthJanitor.Automation.Blazor": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Shared/EntityBasePage.cs b/src/AuthJanitor.Automation.Blazor/Shared/EntityBasePage.cs new file mode 100644 index 0000000..28fc63e --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Shared/EntityBasePage.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor.Shared +{ + public abstract class EntityBasePage : ComponentBase + where TModel : new() + { + protected abstract string UrlBase { get; } + protected abstract Func GetId { get; } + + [Inject] + protected IHttpClientFactory ClientFactory { get; set; } + + protected HttpClient Http => + ClientFactory.CreateClient("AuthJanitorHttpClient"); + + public IEnumerable ModelCollection { get; set; } = new List(); + public TModel SelectedModel { get; set; } = new TModel(); + + protected override Task OnInitializedAsync() => Load(); + + protected async Task> Load() + { + ModelCollection = await Http.GetFromJsonAsync>(UrlBase); + return ModelCollection; + } + + protected Task Create() => Create(SelectedModel); + protected Task Create(TModel model) => + Http.PostAsJsonAsync(UrlBase, model); + + protected Task Update() => Update(SelectedModel); + protected Task Update(TModel model) => + Http.PutAsJsonAsync(UrlBase + $"/{GetId(model)}", model); + + protected Task Delete() => Delete(SelectedModel); + protected Task Delete(TModel model) => + Http.DeleteAsync(UrlBase + $"/{GetId(model)}"); + + protected Task Delete(Guid id) => + Http.DeleteAsync(UrlBase + $"/{id}"); + } +} diff --git a/src/AuthJanitor.Automation.Blazor/Shared/MainLayout.razor b/src/AuthJanitor.Automation.Blazor/Shared/MainLayout.razor new file mode 100644 index 0000000..884eb48 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Shared/MainLayout.razor @@ -0,0 +1,234 @@ +@inherits LayoutComponentBase + + + + + + + + + + AuthJanitor + + + + + + Manage + + + + Dashboard + + + + + + Resources + + + + + + Dependency Groups + + + + + + Rotations + + + System + + + + Providers + + + + + + Integrity + + + + + + + + GitHub + + + + + + Docs + + + + + + + + + + + + + + Example Site + + + + + + + + Welcome @context.User.Identity.Name + + + + + + Theme + + + + + Theme enabled + + + + + + + Gradient colors + + + Rounded elements + + + + + + + + + + + + + + + + + + + @Body + + + + +@inject Microsoft.AspNetCore.Http.IHttpContextAccessor _httpContextAccessor +@code{ + public string UserDisplayName { get; set; } + + protected override async Task OnInitializedAsync() + { + try + { + // get a token + var token = await TokenAcquisitionService.GetAccessTokenForUserAsync( + new string[] { "https://graph.microsoft.com/User.Read" }); + + var http = new HttpClient(); + http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + var dataRequest = await http.GetAsync("https://graph.microsoft.com/beta/me"); + + if (dataRequest.IsSuccessStatusCode) + { + var userData = System.Text.Json.JsonDocument.Parse(await dataRequest.Content.ReadAsStreamAsync()); + UserDisplayName = userData.RootElement.GetProperty("displayName").GetString(); + } + } + catch (Exception ex) + { + ConsentHandler.HandleException(ex); + } + + + } + + void OnThemeEnabledChanged(bool value) + { + if (Theme == null) + return; + + Theme.Enabled = value; + + Theme.ThemeHasChanged(); + } + + void OnGradientChanged(bool value) + { + if (Theme == null) + return; + + Theme.IsGradient = value; + + //if ( Theme.GradientOptions == null ) + // Theme.GradientOptions = new GradientOptions(); + + //Theme.GradientOptions.BlendPercentage = 80; + + Theme.ThemeHasChanged(); + } + + void OnRoundedChanged(bool value) + { + if (Theme == null) + return; + + Theme.IsRounded = value; + + Theme.ThemeHasChanged(); + } + + void OnThemeColorChanged(string value) + { + if (Theme == null) + return; + + if (Theme.ColorOptions == null) + Theme.ColorOptions = new ThemeColorOptions(); + + if (Theme.BackgroundOptions == null) + Theme.BackgroundOptions = new ThemeBackgroundOptions(); + + if (Theme.TextColorOptions == null) + Theme.TextColorOptions = new ThemeTextColorOptions(); + + Theme.ColorOptions.Primary = value; + Theme.BackgroundOptions.Primary = value; + Theme.TextColorOptions.Primary = value; + + if (Theme.InputOptions == null) + Theme.InputOptions = new ThemeInputOptions(); + + //Theme.InputOptions.Color = value; + Theme.InputOptions.CheckColor = value; + Theme.InputOptions.SliderColor = value; + + Theme.ThemeHasChanged(); + } + + private bool topbarVisible = false; + private bool uiElementsVisible = true; + + [CascadingParameter] protected Theme Theme { get; set; } +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/Startup.cs b/src/AuthJanitor.Automation.Blazor/Startup.cs new file mode 100644 index 0000000..805d60f --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/Startup.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using AuthJanitor.Repository; +using Blazorise; +using Blazorise.Bootstrap; +using Blazorise.Icons.FontAwesome; +using McMaster.NETCore.Plugins; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Authorization; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Identity.Web; +using Microsoft.Identity.Web.UI; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.Automation.Blazor +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public delegate Task GetToken(TokenSources src, string parameter); + + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + services.AddMicrosoftIdentityWebApiAuthentication(Configuration) + .EnableTokenAcquisitionToCallDownstreamApi() + .AddInMemoryTokenCaches(); + services.AddMicrosoftIdentityWebAppAuthentication(Configuration) + .EnableTokenAcquisitionToCallDownstreamApi() + .AddInMemoryTokenCaches(); + + services.AddControllersWithViews(options => + { + var policy = new AuthorizationPolicyBuilder() + .RequireAuthenticatedUser() + .Build(); + options.Filters.Add(new AuthorizeFilter(policy)); + }).AddMicrosoftIdentityUI(); + + services.AddScoped(); + + // NOTE: In this mode, the RSA data is ephemeral and cleared at shutdown + services.AddAuthJanitorDummyServices(); + services.AddAuthJanitorService((file) => + PluginLoader.CreateFromAssemblyFile(file, AuthJanitorService.ProviderSharedTypes) + .LoadDefaultAssembly(), + AuthJanitorService.AdminServiceAgentIdentity); + services.AddDbContext(ServiceLifetime.Singleton); + + services.AddRazorPages(); + services.AddServerSideBlazor() + .AddMicrosoftIdentityConsentHandler(); + + services.AddBlazorise() + .AddBootstrapProviders() + .AddFontAwesomeIcons(); + + services.AddHttpClient("AuthJanitorHttpClient", o => + o.BaseAddress = new Uri("https://localhost:44396")); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseAuthentication(); + app.UseAuthorization(); + + app.ApplicationServices + .UseBootstrapProviders() + .UseFontAwesomeIcons() + .TryInitializeDatabase(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapBlazorHub(); + endpoints.MapFallbackToPage("/_Host"); + }); + } + } +} + diff --git a/src/AuthJanitor.Automation.Blazor/_Imports.razor b/src/AuthJanitor.Automation.Blazor/_Imports.razor new file mode 100644 index 0000000..32bc1b2 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/_Imports.razor @@ -0,0 +1,22 @@ +@using System.Net.Http +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop +@using AuthJanitor.Automation.Blazor +@using AuthJanitor.Automation.Blazor.Shared +@using AuthJanitor.Automation.Blazor.Components +@using AuthJanitor.Providers +@using Microsoft.Identity.Web + +@using Blazorise +@using Blazorise.DataGrid + +@using System.Net.Http.Json + +@inject HttpClient Http +@inject AuthJanitorService AuthJanitorService +@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler +@inject ITokenAcquisition TokenAcquisitionService \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/appsettings.Development.json b/src/AuthJanitor.Automation.Blazor/appsettings.Development.json new file mode 100644 index 0000000..5173757 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "DetailedErrors": true, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/AuthJanitor.Automation.Blazor/appsettings.json b/src/AuthJanitor.Automation.Blazor/appsettings.json new file mode 100644 index 0000000..eceeb25 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/appsettings.json @@ -0,0 +1,25 @@ +{ + "AzureAd": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "broadscope.work", + "TenantId": "185bb12a-cb44-4d85-81fe-297ba9e6b4d1", + "ClientId": "6b88ac3e-893f-4b40-9de6-419c70597a11", + "CallbackPath": "/signin-oidc" + }, + "GraphService": { + "BaseUrl": "https://graph.microsoft.com/beta", + "Scopes": "user.read" + }, + "AzureRM": { + "BaseUrl": "https://management.azure.com/", + "Scopes": ".default" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/src/AuthJanitor.Automation.Blazor/wwwroot/css/site.css b/src/AuthJanitor.Automation.Blazor/wwwroot/css/site.css new file mode 100644 index 0000000..af628c6 --- /dev/null +++ b/src/AuthJanitor.Automation.Blazor/wwwroot/css/site.css @@ -0,0 +1,185 @@ +/* Copyright (c) Microsoft Corporation. */ +/* Licensed under the MIT license. */ +@import url('open-iconic/font/css/open-iconic-bootstrap.min.css'); + +html, body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +a, .btn-link { + color: #0366d6; +} + +.btn-primary { + color: #fff; + background-color: #1b6ec2; + border-color: #1861ac; +} + +app { + position: relative; + display: flex; + flex-direction: column; +} + +.top-row { + height: 3.5rem; + display: flex; + align-items: center; +} + +.main { + flex: 1; +} + + .main .top-row { + background-color: #f7f7f7; + border-bottom: 1px solid #d6d5d5; + justify-content: flex-end; + } + + .main .top-row > a, .main .top-row .btn-link { + white-space: nowrap; + margin-left: 1.5rem; + } + +.main .top-row a:first-child { + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebar { + background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); +} + + .sidebar .top-row { + background-color: rgba(0,0,0,0.4); + } + + .sidebar .navbar-brand { + font-size: 1.1rem; + } + + .sidebar .oi { + width: 2rem; + font-size: 1.1rem; + vertical-align: text-top; + top: -2px; + } + + .sidebar .nav-item { + font-size: 0.9rem; + padding-bottom: 0.5rem; + } + + .sidebar .nav-item:first-of-type { + padding-top: 1rem; + } + + .sidebar .nav-item:last-of-type { + padding-bottom: 1rem; + } + + .sidebar .nav-item a { + color: #d7d7d7; + border-radius: 4px; + height: 3rem; + display: flex; + align-items: center; + line-height: 3rem; + } + + .sidebar .nav-item a.active { + background-color: rgba(255,255,255,0.25); + color: white; + } + + .sidebar .nav-item a:hover { + background-color: rgba(255,255,255,0.1); + color: white; + } + +.content { + padding-top: 1.1rem; +} + +.navbar-toggler { + background-color: rgba(255, 255, 255, 0.1); +} + +.valid.modified:not([type=checkbox]) { + outline: 1px solid #26b050; +} + +.invalid { + outline: 1px solid red; +} + +.validation-message { + color: red; +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; +} + +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; +} + +@media (max-width: 767.98px) { + .main .top-row:not(.auth) { + display: none; + } + + .main .top-row.auth { + justify-content: space-between; + } + + .main .top-row a, .main .top-row .btn-link { + margin-left: 0; + } +} + +@media (min-width: 768px) { + app { + flex-direction: row; + } + + .sidebar { + width: 250px; + height: 100vh; + position: sticky; + top: 0; + } + + .main .top-row { + position: sticky; + top: 0; + } + + .main > div { + padding-left: 2rem !important; + padding-right: 1.5rem !important; + } + + .navbar-toggler { + display: none; + } + + .sidebar .collapse { + /* Never collapse the sidebar for wide screens */ + display: block; + } +} diff --git a/src/AuthJanitor.Automation.Blazor/wwwroot/favicon.ico b/src/AuthJanitor.Automation.Blazor/wwwroot/favicon.ico new file mode 100644 index 0000000..a3a7999 Binary files /dev/null and b/src/AuthJanitor.Automation.Blazor/wwwroot/favicon.ico differ diff --git a/src/AuthJanitor.Functions.AdminApi/AuthJanitor.Functions.AdminApi.csproj b/src/AuthJanitor.CLI/AuthJanitor.CLI.csproj similarity index 57% rename from src/AuthJanitor.Functions.AdminApi/AuthJanitor.Functions.AdminApi.csproj rename to src/AuthJanitor.CLI/AuthJanitor.CLI.csproj index 40aeeb6..fd808e3 100644 --- a/src/AuthJanitor.Functions.AdminApi/AuthJanitor.Functions.AdminApi.csproj +++ b/src/AuthJanitor.CLI/AuthJanitor.CLI.csproj @@ -1,28 +1,29 @@  + + Exe netcoreapp3.1 - v3 - Debug;Release - AuthJanitor + + + - + - + + - - - - + + @@ -30,15 +31,5 @@ - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - \ No newline at end of file + + diff --git a/src/AuthJanitor.CLI/AuthenticationConfig.cs b/src/AuthJanitor.CLI/AuthenticationConfig.cs new file mode 100644 index 0000000..3691c62 --- /dev/null +++ b/src/AuthJanitor.CLI/AuthenticationConfig.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using Microsoft.Extensions.Configuration; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace AuthJanitor.CLI +{ + /// + /// Description of the configuration of an AzureAD public client application (desktop/mobile application). This should + /// match the application registration done in the Azure portal + /// + public class AuthenticationConfig + { + /// + /// instance of Azure AD, for example public Azure or a Sovereign cloud (Azure China, Germany, US government, etc ...) + /// + public string Instance { get; set; } = "https://login.microsoftonline.com/{0}"; + + /// + /// The Tenant is: + /// - either the tenant ID of the Azure AD tenant in which this application is registered (a guid) + /// or a domain name associated with the tenant + /// - or 'organizations' (for a multi-tenant application) + /// + public string Tenant { get; set; } + + /// + /// Guid used by the application to uniquely identify itself to Azure AD + /// + public string ClientId { get; set; } + + /// + /// URL of the authority + /// + public string Authority + { + get + { + return String.Format(CultureInfo.InvariantCulture, Instance, Tenant); + } + } + + /// + /// Client secret (application password) + /// + /// Daemon applications can authenticate with AAD through two mechanisms: ClientSecret + /// (which is a kind of application password: this property) + /// or a certificate previously shared with AzureAD during the application registration + /// (and identified by the CertificateName property belows) + /// + public string ClientSecret { get; set; } + + /// + /// Name of a certificate in the user certificate store + /// + /// Daemon applications can authenticate with AAD through two mechanisms: ClientSecret + /// (which is a kind of application password: the property above) + /// or a certificate previously shared with AzureAD during the application registration + /// (and identified by this CertificateName property) + /// + public string CertificateName { get; set; } + + /// + /// Web Api base URL + /// + public string TodoListBaseAddress { get; set; } + + /// + /// Web Api scope. With client credentials flows, the scopes is ALWAYS of the shape "resource/.default" + /// + public string TodoListScope { get; set; } + + /// + /// Reads the configuration from a json file + /// + /// Path to the configuration json file + /// AuthenticationConfig read from the json file + public static AuthenticationConfig ReadFromJsonFile(string path) + { + IConfigurationRoot Configuration; + + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile(path); + + Configuration = builder.Build(); + return Configuration.Get(); + } + } +} diff --git a/src/AuthJanitor.CLI/Program.cs b/src/AuthJanitor.CLI/Program.cs new file mode 100644 index 0000000..85bc438 --- /dev/null +++ b/src/AuthJanitor.CLI/Program.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.CLI.Verbs; +using AuthJanitor.IdentityServices; +using AuthJanitor.Providers; +using AuthJanitor.SecureStorage; +using CommandLine; +using McMaster.NETCore.Plugins; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; + +namespace AuthJanitor.CLI +{ + class Program + { + private const string PROVIDER_SEARCH_MASK = "AuthJanitor.Providers.*.dll"; + private static readonly string PROVIDER_SEARCH_PATH = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..")); + private static readonly Type[] PROVIDER_SHARED_TYPES = new Type[] + { + typeof(IAuthJanitorProvider), + typeof(AuthJanitorProvider<>), + typeof(IServiceCollection), + typeof(ILogger) + }; + + private static ServiceCollection BuildServiceCollection() + { + var serviceCollection = new ServiceCollection(); + var logger = LoggerFactory.Create(builder => + { + builder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Debug) + .AddConsole(); + }).CreateLogger(); + + serviceCollection.AddOptions(); + + serviceCollection.AddAuthJanitorDummyServices(); + + logger.LogDebug("Scanning for Provider modules at {ProviderSearchPath}\\{ProviderSearchMask} recursively", PROVIDER_SEARCH_PATH, PROVIDER_SEARCH_MASK); + + var providerTypes = Directory.GetFiles(PROVIDER_SEARCH_PATH, PROVIDER_SEARCH_MASK, new EnumerationOptions() { RecurseSubdirectories = true }) + .SelectMany(libraryFile => PluginLoader.CreateFromAssemblyFile(libraryFile, PROVIDER_SHARED_TYPES) + .LoadDefaultAssembly() + .GetTypes() + .Where(type => !type.IsAbstract && typeof(IAuthJanitorProvider).IsAssignableFrom(type))) + .ToArray(); + + logger.LogInformation("Found {ProviderCount} providers: {ProviderTypeNames}", providerTypes.Length, string.Join(" ", providerTypes.Select(t => t.Name))); + + logger.LogDebug("Registering AuthJanitor Services"); + serviceCollection.AddAuthJanitorService( + (a) => PluginLoader.CreateFromAssemblyFile(a, AuthJanitorService.ProviderSharedTypes) + .LoadDefaultAssembly(), + "cli"); + + return serviceCollection; + } + + static void Main(string[] args) + { + var serviceProvider = BuildServiceCollection() + .AddLogging() + .BuildServiceProvider(); + + var logger = serviceProvider.GetService().CreateLogger(); + logger.LogDebug("Starting application"); + + var ajService = serviceProvider.GetRequiredService(); + + Parser.Default.ParseArguments< + EnumerateProviderConfigurationsVerb, + RunProviderSetVerb>(args) + .WithParsed(async v => await v.Run(logger, ajService)) + .WithParsed(async v => await v.Run(logger, ajService)); + } + } +} diff --git a/src/AuthJanitor.CLI/Verbs/BaseVerb.cs b/src/AuthJanitor.CLI/Verbs/BaseVerb.cs new file mode 100644 index 0000000..c0d9d47 --- /dev/null +++ b/src/AuthJanitor.CLI/Verbs/BaseVerb.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using Azure.Core; +using Azure.Identity; +using CommandLine; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.CLI.Verbs +{ + public abstract class BaseVerb + { + [Option('u', "user")] + public bool UseUserIdentity { get; set; } + + [Option('s', "system")] + public bool UseSystemIdentity { get; set; } + + [Option("token", HelpText = "Existing access token")] + public string AccessTokenString { get; set; } + + protected async Task EmbedToken() + { + var c = new DefaultAzureCredential(); + var token = await c.GetTokenAsync(new TokenRequestContext( + new[] { "https://management.azure.com/.default" })); + AccessTokenString = token.Token; + } + + protected void WriteError(string error) + { + Console.WriteLine("[ERROR] " + error); + } + } +} diff --git a/src/AuthJanitor.CLI/Verbs/EnumerateProviderConfigurationsVerb.cs b/src/AuthJanitor.CLI/Verbs/EnumerateProviderConfigurationsVerb.cs new file mode 100644 index 0000000..cf968ea --- /dev/null +++ b/src/AuthJanitor.CLI/Verbs/EnumerateProviderConfigurationsVerb.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using CommandLine; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.CLI.Verbs +{ + [Verb("enumerate", HelpText = "Enumerate provider configurations with a given token")] + public class EnumerateProviderConfigurationsVerb : BaseVerb + { + public async Task Run( + ILogger logger, + AuthJanitorService authJanitorService) + { + logger.LogInformation("Getting token"); + await EmbedToken(); + logger.LogInformation("Enumerating (this will take a minute) ..."); + var enumerated = await authJanitorService.EnumerateAsync( + JsonConvert.DeserializeObject(AccessTokenString)); + foreach (var obj in enumerated) + { + Console.WriteLine(obj.Name); + Console.WriteLine(obj.ProviderType); + Console.WriteLine(); + Console.WriteLine(obj.SerializedConfiguration); + Console.WriteLine("\n"); + } + } + } +} diff --git a/src/AuthJanitor.CLI/Verbs/RunProviderSetVerb.cs b/src/AuthJanitor.CLI/Verbs/RunProviderSetVerb.cs new file mode 100644 index 0000000..a225d26 --- /dev/null +++ b/src/AuthJanitor.CLI/Verbs/RunProviderSetVerb.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using CommandLine; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace AuthJanitor.CLI.Verbs +{ + [Verb("run", HelpText = "Execute provider configurations in the proper order with a given token")] + public class RunProviderSetVerb : BaseVerb + { + [Option("file", HelpText = "Path to file containing provider configurations")] + public string ProviderConfigurationFile { get; set; } + + public async Task Run( + ILogger logger, + AuthJanitorService authJanitorService) + { + var loadedFile = File.ReadAllText(ProviderConfigurationFile); + var parameters = JsonConvert.DeserializeObject>(loadedFile); + + if (parameters == null) + { + logger.LogError("Could not load configuration file"); + return; + } + + var validPeriod = TimeSpan.FromDays(1); + + if (parameters.Any(p => p.AccessToken == null)) + { + logger.LogInformation("Getting token"); + await EmbedToken(); + var token = JsonConvert.DeserializeObject(AccessTokenString); + foreach (var p in parameters.Where(p => p.AccessToken == null)) + p.AccessToken = token; + } + + var currentLog = string.Empty; + Console.WriteLine("Running "); + await authJanitorService.ExecuteAsync( + validPeriod, + async (c) => { + if (c.HasBeenExecuted) + { + Console.WriteLine(); + foreach (var item in c.Actions) + { + Console.WriteLine($"{item.Start} => {item.End}"); + Console.WriteLine(item.Log); + if (!item.HasSucceeded) + WriteError($"Action failed to execute: {item.Exception}"); + } + Console.WriteLine(); + Console.WriteLine("Orchestration Log:"); + Console.WriteLine(c.OrchestrationLog); + Console.WriteLine(); + } + else + Console.Write("."); + await Task.Yield(); + }, + parameters.ToArray()); + } + } +} diff --git a/src/AuthJanitor.Core/AccessTokenCredential.cs b/src/AuthJanitor.Core/AccessTokenCredential.cs index 5ddc823..72ed2b4 100644 --- a/src/AuthJanitor.Core/AccessTokenCredential.cs +++ b/src/AuthJanitor.Core/AccessTokenCredential.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using Newtonsoft.Json; using System; +using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; namespace AuthJanitor @@ -70,6 +72,14 @@ public DateTimeOffset NotBeforeDateTime public string SubErrorType { get; set; } [JsonPropertyName("error_codes")] + [NotMapped] public int[] ErrorCodes { get; set; } + + public static AccessTokenCredential CreateBearer(string token) => + new AccessTokenCredential() + { + TokenType = "Bearer", + AccessToken = token + }; } } diff --git a/src/AuthJanitor.Core/Agents/DummyAgentCommunicationProvider.cs b/src/AuthJanitor.Core/Agents/DummyAgentCommunicationProvider.cs new file mode 100644 index 0000000..41addbf --- /dev/null +++ b/src/AuthJanitor.Core/Agents/DummyAgentCommunicationProvider.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Agents; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; + +namespace AuthJanitor.IdentityServices +{ + public static class DummyAgentCommunicationProviderExtensions + { + public static void AddDummyAgentCommunicationProvider(this IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + } + } + + public class DummyAgentCommunicationProvider : IAgentCommunicationProvider + { + public Task Send(AgentMessageEnvelope envelope) => Task.FromResult(false); + + public Task TryReceive() => Task.FromResult(new AgentMessageEnvelope()); + } +} diff --git a/src/AuthJanitor.Core/Agents/IAgentCommunicationProvider.cs b/src/AuthJanitor.Core/Agents/IAgentCommunicationProvider.cs index 5ef8c20..48731b6 100644 --- a/src/AuthJanitor.Core/Agents/IAgentCommunicationProvider.cs +++ b/src/AuthJanitor.Core/Agents/IAgentCommunicationProvider.cs @@ -6,7 +6,7 @@ namespace AuthJanitor.Agents { public interface IAgentCommunicationProvider { - Task Send(AgentMessageEnvelope envelop); + Task Send(AgentMessageEnvelope envelope); Task TryReceive(); } } diff --git a/src/AuthJanitor.Core/AuthJanitorService.cs b/src/AuthJanitor.Core/AuthJanitorService.cs index e6db57b..c39c265 100644 --- a/src/AuthJanitor.Core/AuthJanitorService.cs +++ b/src/AuthJanitor.Core/AuthJanitorService.cs @@ -3,8 +3,6 @@ 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; @@ -24,57 +22,60 @@ 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(); - serviceCollection.AddSingleton(); - - serviceCollection.AddSingleton((s) => - new ProviderManagerService(s, providerTypes)); - - serviceCollection.Configure((o) => o.InstanceId = instanceIdentity); - serviceCollection.AddSingleton(); - - serviceCollection.AddTransient(); - serviceCollection.AddTransient(typeof(ProviderWorkflowActionLogger<>), typeof(ProviderWorkflowActionLogger<>)); - } - } - public class AuthJanitorService { private readonly ILogger _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 _options; + private readonly ITokenCredentialProvider _tokenCredentialProvider; + + public static readonly Type[] ProviderSharedTypes = new Type[] + { + typeof(IAuthJanitorProvider), + typeof(AuthJanitorProvider<>), + typeof(IServiceCollection), + typeof(ILogger) + }; + public const string AdminServiceAgentIdentity = "agent-service"; public AuthJanitorService( ILogger logger, ProviderManagerService providerManagerService, ICryptographicImplementation cryptographicImplementation, ISecureStorage secureStorage, - IIdentityService identityService, EventDispatcherService eventDispatcherService, IAgentCommunicationProvider agentCommunicationProvider, - IOptions options) + IOptions options, + ITokenCredentialProvider tokenCredentialProvider) { _logger = logger; _providerManagerService = providerManagerService; _cryptographicImplementation = cryptographicImplementation; _secureStorage = secureStorage; - _identityService = identityService; _eventDispatcher = eventDispatcherService; _agentCommunicationProvider = agentCommunicationProvider; _options = options; + _tokenCredentialProvider = tokenCredentialProvider; } + public ITokenCredentialProvider TokenCredentialProvider => _tokenCredentialProvider; + public ISecureStorage SecureStorage => _secureStorage; + public EventDispatcherService EventDispatcher => _eventDispatcher; + public ProviderManagerService ProviderManager => _providerManagerService; + public IAgentCommunicationProvider AgentCommunicationProvider => _agentCommunicationProvider; + public string AgentId => _options.Value.InstanceId; + + /// + /// Get a list of the possible Providers which can be used inside + /// this instance of the AuthJanitor service + /// + public IReadOnlyList LoadedProviders => + _providerManagerService.LoadedProviders; + /// /// Process an incoming serialized message. /// @@ -125,28 +126,6 @@ await ExecuteAsync(message.ValidPeriod, } } - /// - /// Stash the credentials for the current user in the preferred - /// secure storage, and return the Guid of the stashed object. - /// - /// When the credentials expire, if unused - /// Guid of stashed object - public async Task StashCredentialForCurrentUserAsync(DateTimeOffset expiry = default) => - await StashCredentialAsync( - await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync(), - expiry); - - /// - /// Stash the credentials for the application's identity in the preferred - /// secure storage, and return the Guid of the stashed object. - /// - /// When the credentials expire, if unused - /// Guid of stashed object - public async Task StashCredentialForCurrentAppAsync(DateTimeOffset expiry = default) => - await StashCredentialAsync( - await _identityService.GetAccessTokenForApplicationAsync(), - expiry); - /// /// Stash a given credentials object in the preferred /// secure storage, and return the Guid of the stashed object. @@ -228,62 +207,13 @@ public async Task ExecuteAsync( new ProviderWorkflowActionCollection(); try { - var persisted = new Dictionary(); - if (providerConfigurations.Any(p => p.TokenSource == TokenSources.Persisted)) + await Task.WhenAll(providerConfigurations.Select(async provider => { - _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(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(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; + provider.AccessToken = await _tokenCredentialProvider.GetToken( + provider.TokenSource, + provider.TokenParameter); })); - // --- end access token acquisition/embed --- - var providers = providerConfigurations.Select(r => { var p = _providerManagerService.GetProviderInstance( @@ -351,5 +281,14 @@ await _eventDispatcher.DispatchEvent( return workflowCollection; } } + + /// + /// Enumerate all available Provider configurations with a given AccessTokenCredential + /// + /// Credential to enumerate from + /// Collection of ProviderResourceSuggestions + public Task> EnumerateAsync( + AccessTokenCredential credential) => + _providerManagerService.EnumerateProviders(credential); } } diff --git a/src/AuthJanitor.Core/AuthJanitorServiceExtensions.cs b/src/AuthJanitor.Core/AuthJanitorServiceExtensions.cs new file mode 100644 index 0000000..c5b1f30 --- /dev/null +++ b/src/AuthJanitor.Core/AuthJanitorServiceExtensions.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.CryptographicImplementations; +using AuthJanitor.IdentityServices; +using AuthJanitor.Providers; +using AuthJanitor.SecureStorage; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace AuthJanitor +{ + public static class AuthJanitorServiceExtensions + { + private const string PROVIDER_SEARCH_MASK = "AuthJanitor.Providers.*.dll"; + private static readonly string PROVIDER_SEARCH_PATH = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..")); + + public static void AddAuthJanitorService(this IServiceCollection serviceCollection, + Func assemblyLoaderCallback, + string instanceIdentity) + { + serviceCollection.AddSingleton((s) => new ProviderManagerService(s, + LoadAssembliesFromLocalPath(assemblyLoaderCallback))); + + serviceCollection.Configure((o) => o.InstanceId = instanceIdentity); + + serviceCollection.RegisterAuthJanitorRollupServices(); + } + + public static void AddAuthJanitorDummyServices + (this IServiceCollection serviceCollection) + { + serviceCollection.AddAJDefaultCryptographicImplementation + (o => + { + o.EmbedEphemeralRSAKey(); + }); + serviceCollection.AddDummyAgentCommunicationProvider(); + serviceCollection.AddDummySecureStorage(); + } + + private static void RegisterAuthJanitorRollupServices(this IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + //serviceCollection.AddSingleton(); + + serviceCollection.AddTransient(); + serviceCollection.AddTransient(typeof(ProviderWorkflowActionLogger<>), typeof(ProviderWorkflowActionLogger<>)); + + serviceCollection.AddScoped(); + + } + + private static Type[] LoadAssembliesFromLocalPath(Func assemblyLoaderCallback) => + Directory.GetFiles( + PROVIDER_SEARCH_PATH, + PROVIDER_SEARCH_MASK, + new EnumerationOptions() { RecurseSubdirectories = true }) + .SelectMany(libraryFile => assemblyLoaderCallback(libraryFile) + .GetTypes() + .Where(type => !type.IsAbstract && typeof(IAuthJanitorProvider).IsAssignableFrom(type))) + .ToArray(); + } +} diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/DefaultCryptographicImplementation.cs b/src/AuthJanitor.Core/CryptographicImplementations/DefaultCryptographicImplementation.cs similarity index 98% rename from src/AuthJanitor.Integrations.CryptographicImplementations.Default/DefaultCryptographicImplementation.cs rename to src/AuthJanitor.Core/CryptographicImplementations/DefaultCryptographicImplementation.cs index 8bafa19..0cc7923 100644 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/DefaultCryptographicImplementation.cs +++ b/src/AuthJanitor.Core/CryptographicImplementations/DefaultCryptographicImplementation.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -using AuthJanitor.CryptographicImplementations; using Microsoft.Extensions.Options; using System; using System.IO; @@ -9,7 +8,7 @@ using System.Text; using System.Threading.Tasks; -namespace AuthJanitor.Integrations.CryptographicImplementations.Default +namespace AuthJanitor.CryptographicImplementations { /// /// The default AuthJanitor cryptographic implementation. diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/DefaultCryptographicImplementationConfiguration.cs b/src/AuthJanitor.Core/CryptographicImplementations/DefaultCryptographicImplementationConfiguration.cs similarity index 88% rename from src/AuthJanitor.Integrations.CryptographicImplementations.Default/DefaultCryptographicImplementationConfiguration.cs rename to src/AuthJanitor.Core/CryptographicImplementations/DefaultCryptographicImplementationConfiguration.cs index 8599304..053bfdc 100644 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/DefaultCryptographicImplementationConfiguration.cs +++ b/src/AuthJanitor.Core/CryptographicImplementations/DefaultCryptographicImplementationConfiguration.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; -namespace AuthJanitor.Integrations.CryptographicImplementations.Default +namespace AuthJanitor.CryptographicImplementations { public class DefaultCryptographicImplementationConfiguration { diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/Extensions.cs b/src/AuthJanitor.Core/CryptographicImplementations/Extensions.cs similarity index 58% rename from src/AuthJanitor.Integrations.CryptographicImplementations.Default/Extensions.cs rename to src/AuthJanitor.Core/CryptographicImplementations/Extensions.cs index 64ef3c2..f0b4ee0 100644 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/Extensions.cs +++ b/src/AuthJanitor.Core/CryptographicImplementations/Extensions.cs @@ -1,13 +1,22 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -using AuthJanitor.CryptographicImplementations; using Microsoft.Extensions.DependencyInjection; using System; +using System.Security.Cryptography; -namespace AuthJanitor.Integrations.CryptographicImplementations.Default +namespace AuthJanitor.CryptographicImplementations { public static class Extensions { + public static DefaultCryptographicImplementationConfiguration EmbedEphemeralRSAKey( + this DefaultCryptographicImplementationConfiguration config) + { + var rsa = RSA.Create(); + config.PublicKey = rsa.ExportRSAPublicKey(); + config.PrivateKey = rsa.ExportRSAPrivateKey(); + return config; + } + public static void AddAJDefaultCryptographicImplementation(this IServiceCollection serviceCollection, Action configureOptions) { serviceCollection.Configure(configureOptions); diff --git a/src/AuthJanitor.Core/ITokenCredentialProvider.cs b/src/AuthJanitor.Core/ITokenCredentialProvider.cs new file mode 100644 index 0000000..4afe88a --- /dev/null +++ b/src/AuthJanitor.Core/ITokenCredentialProvider.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using System.Threading.Tasks; + +namespace AuthJanitor +{ + public interface ITokenCredentialProvider + { + public Task GetToken( + TokenSources source, + string parameters); + } +} diff --git a/src/AuthJanitor.Core/IdentityServices/IIdentityService.cs b/src/AuthJanitor.Core/IdentityServices/IIdentityService.cs deleted file mode 100644 index a2f6cce..0000000 --- a/src/AuthJanitor.Core/IdentityServices/IIdentityService.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System.Threading.Tasks; - -namespace AuthJanitor.IdentityServices -{ - public interface IIdentityService : IAuthJanitorExtensibilityPoint - { - /// - /// Return if there is currently a user logged in (with any valid AuthJanitor role) - /// - bool IsUserLoggedIn { get; } - - /// - /// Return the current user's e-mail address - /// - string UserEmail { get; } - - /// - /// Return the current user's name - /// - string UserName { get; } - - /// - /// Return a list of the current user's roles - /// - string[] UserRoles { get; } - - /// - /// If the currently logged in user has the given role (or is a GlobalAdmin) - /// - /// Role to test - bool CurrentUserHasRole(string authJanitorRole); - - /// - /// Retrieve the Access Token for the current underlying MSI/SP identity - /// - /// Scopes to request with Access Token - /// Access Token - Task GetAccessTokenForApplicationAsync(params string[] scopes); - - /// - /// Retrieve an Access Token for a resource on behalf of the current user - /// - /// Resource to perform on-behalf-of exchange for - /// Access Token - Task GetAccessTokenOnBehalfOfCurrentUserAsync(string resource = "https://management.core.windows.net"); - } -} diff --git a/src/AuthJanitor.Core/Integrity/IntegrityReportExtensibilityType.cs b/src/AuthJanitor.Core/Integrity/IntegrityReportExtensibilityType.cs index 004609b..2e0a923 100644 --- a/src/AuthJanitor.Core/Integrity/IntegrityReportExtensibilityType.cs +++ b/src/AuthJanitor.Core/Integrity/IntegrityReportExtensibilityType.cs @@ -30,10 +30,6 @@ public IntegrityReportExtensibilityType(Type type) { ExtensibilityType = ExtensibilityTypes.Provider; } - else if (typeof(IdentityServices.IIdentityService).IsAssignableFrom(type)) - { - ExtensibilityType = ExtensibilityTypes.Identity; - } else if (typeof(CryptographicImplementations.ICryptographicImplementation).IsAssignableFrom(type)) { ExtensibilityType = ExtensibilityTypes.CryptographicImplementation; diff --git a/src/AuthJanitor.Core/Providers/ProviderExecutionParameters.cs b/src/AuthJanitor.Core/Providers/ProviderExecutionParameters.cs index cf3fdee..15d1a51 100644 --- a/src/AuthJanitor.Core/Providers/ProviderExecutionParameters.cs +++ b/src/AuthJanitor.Core/Providers/ProviderExecutionParameters.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using System.ComponentModel.DataAnnotations.Schema; + namespace AuthJanitor.Providers { public enum TokenSources @@ -20,6 +22,7 @@ public class ProviderExecutionParameters public string TokenParameter { get; set; } public string AgentId { get; set; } = "admin-service"; + [NotMapped] public AccessTokenCredential AccessToken { get; set; } } } diff --git a/src/AuthJanitor.Core/Providers/ProviderResourceSuggestion.cs b/src/AuthJanitor.Core/Providers/ProviderResourceSuggestion.cs index 362d2f6..6c79a9d 100644 --- a/src/AuthJanitor.Core/Providers/ProviderResourceSuggestion.cs +++ b/src/AuthJanitor.Core/Providers/ProviderResourceSuggestion.cs @@ -16,6 +16,12 @@ public class ProviderResourceSuggestion public AuthJanitorProviderConfiguration Configuration { get; set; } public string SerializedConfiguration { get; set; } + public ProviderExecutionParameters Parameters => + new ProviderExecutionParameters() + { + ProviderType = ProviderType, + ProviderConfiguration = SerializedConfiguration, + }; public ProviderResourceSuggestion() { diff --git a/src/AuthJanitor.Core/SecureStorage/DummySecureStorage.cs b/src/AuthJanitor.Core/SecureStorage/DummySecureStorage.cs new file mode 100644 index 0000000..b229552 --- /dev/null +++ b/src/AuthJanitor.Core/SecureStorage/DummySecureStorage.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Threading.Tasks; + +namespace AuthJanitor.SecureStorage +{ + public static class DummySecureStorageExtensions + { + public static void AddDummySecureStorage(this IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + } + } + public class DummySecureStorage : ISecureStorage + { + public Task Destroy(Guid persistenceId) => Task.FromResult(false); + + public Task Persist(DateTimeOffset expiry, T persistedObject) => Task.FromResult(Guid.Empty); + + public Task Retrieve(Guid persistenceId) => Task.FromResult(default(T)); + } +} diff --git a/src/AuthJanitor.Functions.AdminApi/.gitignore b/src/AuthJanitor.Functions.AdminApi/.gitignore deleted file mode 100644 index ff5b00c..0000000 --- a/src/AuthJanitor.Functions.AdminApi/.gitignore +++ /dev/null @@ -1,264 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# Azure Functions localsettings file -local.settings.json - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc \ No newline at end of file diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/AccessManagement.cs b/src/AuthJanitor.Functions.AdminApi/Functions/AccessManagement.cs deleted file mode 100644 index 3e21b72..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/AccessManagement.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.IdentityServices; -using AuthJanitor.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Newtonsoft.Json; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - public class AccessManagement - { - private readonly IIdentityService _identityService; - private readonly IdentityManagementService _managementService; - - public AccessManagement( - IIdentityService identityService, - IdentityManagementService managementService) - { - _identityService = identityService; - _managementService = managementService; - } - - [FunctionName("AccessManagement-Add")] - public async Task Add([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "access")] string userJson) //AuthJanitorAuthorizedUser newAuthorizedUserRole) - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - var newAuthorizedUserRole = JsonConvert.DeserializeObject(userJson); - - return await _managementService.AddAuthorizedUser(newAuthorizedUserRole.UPN, newAuthorizedUserRole.RoleValue); - } - - [FunctionName("AccessManagement-List")] - public async Task List([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "access")] HttpRequest req) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - return await _managementService.GetAuthorizedUsers(); - } - - [FunctionName("AccessManagement-RemoveRole")] - public async Task RemoveRole([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "access/removeRole")] AuthJanitorAuthorizedUser userToBeRemoved) - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - return await _managementService.RemoveAuthorizedUser(userToBeRemoved.UPN, userToBeRemoved.RoleValue); - } - - [FunctionName("AccessManagement-RemoveUser")] - public async Task RemoveUser([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "access/removeUser")] AuthJanitorAuthorizedUser userToBeRemoved) - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - return await _managementService.RemoveAuthorizedUser(userToBeRemoved.UPN); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/Dashboard.cs b/src/AuthJanitor.Functions.AdminApi/Functions/Dashboard.cs deleted file mode 100644 index dd41ff8..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/Dashboard.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - public class Dashboard - { - private readonly DashboardService _service; - - public Dashboard(DashboardService service) - { - _service = service; - } - - [FunctionName("Dashboard")] - public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "dashboard")] HttpRequest req, CancellationToken cancellationToken) - { - return await _service.Run(req, cancellationToken); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/ManagedSecrets.cs b/src/AuthJanitor.Functions.AdminApi/Functions/ManagedSecrets.cs deleted file mode 100644 index 738124e..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/ManagedSecrets.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using AuthJanitor.UI.Shared.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Newtonsoft.Json; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - /// - /// API functions to control the creation and management of AuthJanitor Managed Secrets. - /// A Managed Secret is a grouping of Resources and Policies which describe the strategy around rekeying an object and the applications which consume it. - /// - public class ManagedSecrets - { - private readonly ManagedSecretsService _service; - - public ManagedSecrets(ManagedSecretsService service) - { - _service = service; - } - - [FunctionName("ManagedSecrets-Create")] - public async Task Create([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "managedSecrets")] string secretJson, //ManagedSecretViewModel inputSecret, - CancellationToken cancellationToken) - { - var inputSecret = JsonConvert.DeserializeObject(secretJson); - return await _service.Create(inputSecret, cancellationToken); - } - - [FunctionName("ManagedSecrets-List")] - public async Task List([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "managedSecrets")] HttpRequest req, CancellationToken cancellationToken) - { - return await _service.List(req, cancellationToken); - } - - [FunctionName("ManagedSecrets-Get")] - public async Task Get([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "managedSecrets/{secretId}")] HttpRequest req, - string secretId, CancellationToken cancellationToken) - { - return await _service.Get(req, Guid.Parse(secretId), cancellationToken); - } - - [FunctionName("ManagedSecrets-Delete")] - public async Task Delete([HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "managedSecrets/{secretId}")] HttpRequest req, - string secretId, CancellationToken cancellationToken) - { - return await _service.Delete(req, Guid.Parse(secretId), cancellationToken); - } - - [FunctionName("ManagedSecrets-Update")] - public async Task Update( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "managedSecrets/{secretId}")] string secretJson, //ManagedSecretViewModel inputSecret, - string secretId, CancellationToken cancellationToken) - { - var inputSecret = JsonConvert.DeserializeObject(secretJson); - return await _service.Update(inputSecret, Guid.Parse(secretId), cancellationToken); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/Providers.cs b/src/AuthJanitor.Functions.AdminApi/Functions/Providers.cs deleted file mode 100644 index 34de0bf..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/Providers.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - /// - /// API functions to describe the loaded Providers and their configurations. - /// A Provider is a library containing logic to either rekey an object/service or manage the lifecycle of an application. - /// - public class Providers - { - private readonly ProvidersService _service; - - public Providers(ProvidersService service) - { - _service = service; - } - - [FunctionName("Providers-List")] - public IActionResult List([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "providers")] HttpRequest req) - { - return _service.List(req); - } - - [FunctionName("Providers-Enumerate")] - public async Task Enumerate([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "providers/enumerate")] HttpRequest req) - { - return await _service.Enumerate(req); - } - - [FunctionName("Providers-GetBlankConfiguration")] - public async Task GetBlankConfiguration( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "providers/{providerType}")] HttpRequest req, - string providerType) - { - return await _service.GetBlankConfiguration(req, providerType); - } - - [FunctionName("Providers-TestConfiguration")] - public async Task TestConfiguration( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "providers/{providerType}/test/{testContext}")] string providerConfiguration, - HttpRequest req, - string providerType, - string testContext) - { - return await _service.TestConfiguration(providerConfiguration, req, providerType, testContext); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/RekeyingTasks.cs b/src/AuthJanitor.Functions.AdminApi/Functions/RekeyingTasks.cs deleted file mode 100644 index 9598d33..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/RekeyingTasks.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - /// - /// API functions to control the creation management, and approval of Rekeying Tasks. - /// A Rekeying Task is a time-bounded description of one or more Managed Secrets to be rekeyed. - /// - public class RekeyingTasks - { - private readonly RekeyingTasksService _service; - - public RekeyingTasks(RekeyingTasksService service) - { - _service = service; - } - - [FunctionName("RekeyingTasks-Create")] - public async Task Create( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "tasks/{secretId}")] HttpRequest req, - string secretId, CancellationToken cancellationToken) - { - return await _service.Create(req, Guid.Parse(secretId), cancellationToken); - } - - [FunctionName("RekeyingTasks-List")] - public async Task List([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "tasks")] HttpRequest req, CancellationToken cancellationToken) - { - return await _service.List(req, cancellationToken); - } - - [FunctionName("RekeyingTasks-Get")] - public async Task Get([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "tasks/{taskId}")] HttpRequest req, - string taskId, CancellationToken cancellationToken) - { - return await _service.Get(req, Guid.Parse(taskId), cancellationToken); - } - - [FunctionName("RekeyingTasks-Delete")] - public async Task Delete([HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "tasks/{taskId}")] HttpRequest req, - string taskId, CancellationToken cancellationToken) - { - return await _service.Delete(req, Guid.Parse(taskId), cancellationToken); - } - - [FunctionName("RekeyingTasks-Approve")] - public async Task Approve([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "tasks/{taskId}/approve")] HttpRequest req, - string taskId, CancellationToken cancellationToken) - { - return await _service.Approve(req, Guid.Parse(taskId), cancellationToken); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/Resources.cs b/src/AuthJanitor.Functions.AdminApi/Functions/Resources.cs deleted file mode 100644 index 0b42681..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/Resources.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using AuthJanitor.UI.Shared.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Newtonsoft.Json; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - /// - /// API functions to control the creation and management of AuthJanitor Resources. - /// A Resource is the description of how to connect to an object or resource, using a given Provider. - /// - public class Resources - { - private readonly ResourcesService _service; - - public Resources(ResourcesService service) - { - _service = service; - } - - [FunctionName("Resources-Create")] - public async Task Create( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "resources")] string resourceJson, /*ResourceViewModel resource,*/ - HttpRequest req, CancellationToken cancellationToken) - { - var resource = JsonConvert.DeserializeObject(resourceJson); - return await _service.Create(resource, req, cancellationToken); - } - - [FunctionName("Resources-List")] - public async Task List([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "resources")] HttpRequest req, CancellationToken cancellationToken) - { - return await _service.List(req, cancellationToken); - } - - [FunctionName("Resources-Get")] - public async Task Get( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "resources/{resourceId}")] HttpRequest req, - string resourceId, CancellationToken cancellationToken) - { - return await _service.Get(req, Guid.Parse(resourceId), cancellationToken); - } - - [FunctionName("Resources-Delete")] - public async Task Delete( - [HttpTrigger(AuthorizationLevel.Anonymous, "delete", Route = "resources/{resourceId}")] HttpRequest req, - string resourceId, CancellationToken cancellationToken) - { - return await _service.Delete(req, Guid.Parse(resourceId), cancellationToken); - } - - [FunctionName("Resources-Update")] - public async Task Update( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "resources/{resourceId}")] string resourceJson, //ResourceViewModel resource, - HttpRequest req, - string resourceId, CancellationToken cancellationToken) - { - var resource = JsonConvert.DeserializeObject(resourceJson); - return await _service.Update(resource, req, Guid.Parse(resourceId), cancellationToken); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/ScheduleRekeyingTasks.cs b/src/AuthJanitor.Functions.AdminApi/Functions/ScheduleRekeyingTasks.cs deleted file mode 100644 index f3be8f8..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/ScheduleRekeyingTasks.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using Microsoft.Azure.WebJobs; -using Microsoft.Extensions.Logging; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - public class ScheduleRekeyingTasks - { - private readonly ScheduleRekeyingTasksService _service; - - public ScheduleRekeyingTasks(ScheduleRekeyingTasksService service) - { - _service = service; - } - - [FunctionName("ScheduleRekeyingTasks")] - public async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer, ILogger log, CancellationToken cancellationToken) - { - await _service.Run(myTimer, log, cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.AdminApi/Functions/System.cs b/src/AuthJanitor.Functions.AdminApi/Functions/System.cs deleted file mode 100644 index 589713c..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Functions/System.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Services; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Functions -{ - public class System - { - private readonly SystemService _service; - - public System(SystemService service) - { - _service = service; - } - - [FunctionName("System-GetIntegrityReports")] - public async Task GetIntegrityReports( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "system/integrityReports")] HttpRequest req, - CancellationToken cancellationToken) - { - return await _service.GetIntegrityReports(req, cancellationToken); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Properties/launchSettings.json b/src/AuthJanitor.Functions.AdminApi/Properties/launchSettings.json deleted file mode 100644 index da8579a..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "profiles": { - "AuthJanitor.Functions.AdminApi": { - "commandName": "Project", - "commandLineArgs": "host start --pause-on-error --port 16100", - "sqlDebugging": false - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.AdminApi/Services/DashboardService.cs b/src/AuthJanitor.Functions.AdminApi/Services/DashboardService.cs deleted file mode 100644 index 995f60e..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/DashboardService.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.IdentityServices; -using AuthJanitor.Integrations.DataStores; -using AuthJanitor.Providers; -using AuthJanitor.UI.Shared; -using AuthJanitor.UI.Shared.Models; -using AuthJanitor.UI.Shared.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Services -{ - public class DashboardService - { - private readonly IDataStore _managedSecrets; - private readonly IDataStore _resources; - private readonly IDataStore _rekeyingTasks; - - private readonly Func _managedSecretViewModel; - - private readonly IIdentityService _identityService; - private readonly ProviderManagerService _providerManager; - - public DashboardService( - IIdentityService identityService, - ProviderManagerService providerManager, - IDataStore managedSecretStore, - IDataStore resourceStore, - IDataStore rekeyingTaskStore, - Func managedSecretViewModelDelegate) - { - _identityService = identityService; - _providerManager = providerManager; - - _managedSecrets = managedSecretStore; - _resources = resourceStore; - _rekeyingTasks = rekeyingTaskStore; - - _managedSecretViewModel = managedSecretViewModelDelegate; - } - - public async Task Run(HttpRequest req, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - var allSecrets = await _managedSecrets.Get(cancellationToken); - var allResources = await _resources.Get(cancellationToken); - var allTasks = await _rekeyingTasks.Get(cancellationToken); - - var expiringInNextWeek = allSecrets.Where(s => DateTimeOffset.UtcNow.AddDays(7) < (s.LastChanged + s.ValidPeriod)); - var expired = allSecrets.Where(s => !s.IsValid); - - var metrics = new DashboardMetricsViewModel() - { - SignedInName = _identityService.UserName, - SignedInEmail = _identityService.UserEmail, - SignedInRoles = string.Join(", ", _identityService.UserRoles), - TotalResources = allResources.Count, - TotalSecrets = allSecrets.Count, - TotalPendingApproval = allTasks.Where(t => - t.ConfirmationType.HasFlag(TaskConfirmationStrategies.AdminCachesSignOff) || - t.ConfirmationType.HasFlag(TaskConfirmationStrategies.AdminSignsOffJustInTime)).Count(), - TotalExpiringSoon = expiringInNextWeek.Count(), - TotalExpired = expired.Count(), - ExpiringSoon = expiringInNextWeek.Select(s => _managedSecretViewModel(s)), - PercentExpired = (int)((double)expired.Count() / allSecrets.Count) * 100, - TasksInError = allTasks.Count(t => t.RekeyingFailed) - }; - - foreach (var secret in allSecrets) - { - var riskScore = 0; - foreach (var resourceId in secret.ResourceIds) - { - var resource = allResources.FirstOrDefault(r => r.ObjectId == resourceId); - - var provider = _providerManager.GetProviderInstance( - resource.ProviderType, - resource.ProviderConfiguration); - riskScore += provider.GetRisks(secret.ValidPeriod).Sum(r => r.Score); - } - if (riskScore > 85) - metrics.RiskOver85++; - else if (riskScore > 60) - metrics.Risk85++; - else if (riskScore > 35) - metrics.Risk60++; - else if (riskScore > 0) - metrics.Risk35++; - else if (riskScore == 0) - metrics.Risk0++; - } - - return new OkObjectResult(metrics); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Services/IdentityManagementService.cs b/src/AuthJanitor.Functions.AdminApi/Services/IdentityManagementService.cs deleted file mode 100644 index 623dd1b..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/IdentityManagementService.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.IdentityServices; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Threading.Tasks; - -namespace AuthJanitor.Services -{ - public class IdentityManagementService - { - private readonly IIdentityService _identityService; - private readonly IIdentityServiceManager _identityServiceManager; - - public IdentityManagementService( - IIdentityService identityService, - IIdentityServiceManager identityServiceManager) - { - _identityService = identityService; - _identityServiceManager = identityServiceManager; - } - - public async Task GetAuthorizedUsers() - { - try - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - return new OkObjectResult(await _identityServiceManager.GetAuthorizedUsers()); - } - catch (Exception ex) - { - return new BadRequestObjectResult(ex); - } - } - - public async Task AddAuthorizedUser(string userPrincipalName, string role) - { - try - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - await _identityServiceManager.AddAuthorizedUser( - System.Net.WebUtility.UrlEncode(userPrincipalName), - role); - } - catch (Exception ex) - { - return new BadRequestObjectResult(ex); - } - return new OkResult(); - } - - public async Task RemoveAuthorizedUser(string userPrincipalName) - { - try - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - await _identityServiceManager.RemoveAuthorizedUser(System.Net.WebUtility.UrlEncode(userPrincipalName)); - } - catch (Exception ex) - { - return new BadRequestObjectResult(ex); - } - return new OkResult(); - } - - public async Task RemoveAuthorizedUser(string userPrincipalName, string role) - { - try - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.GlobalAdmin)) return new UnauthorizedResult(); - - await _identityServiceManager.RemoveAuthorizedUser( - System.Net.WebUtility.UrlEncode(userPrincipalName), - role); - } - catch (Exception ex) - { - return new BadRequestObjectResult(ex); - } - return new OkResult(); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Services/ManagedSecretsService.cs b/src/AuthJanitor.Functions.AdminApi/Services/ManagedSecretsService.cs deleted file mode 100644 index 38ac3f8..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/ManagedSecretsService.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.EventSinks; -using AuthJanitor.IdentityServices; -using AuthJanitor.UI.Shared.Models; -using AuthJanitor.UI.Shared.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using AuthJanitor.CryptographicImplementations; -using AuthJanitor.DataStores; - -namespace AuthJanitor.Services -{ - /// - /// API functions to control the creation and management of AuthJanitor Managed Secrets. - /// A Managed Secret is a grouping of Resources and Policies which describe the strategy around rekeying an object and the applications which consume it. - /// - public class ManagedSecretsService - { - private readonly AuthJanitorCoreConfiguration _configuration; - private readonly IIdentityService _identityService; - private readonly ICryptographicImplementation _cryptographicImplementation; - private readonly ProviderManagerService _providerManager; - private readonly EventDispatcherService _eventDispatcher; - - private readonly IDataStore _managedSecrets; - private readonly IDataStore _resources; - private readonly Func _managedSecretViewModel; - - public ManagedSecretsService( - IOptions configuration, - IIdentityService identityService, - ICryptographicImplementation cryptographicImplementation, - EventDispatcherService eventDispatcher, - ProviderManagerService providerManager, - IDataStore managedSecretStore, - IDataStore resourceStore, - Func managedSecretViewModelDelegate) - { - _configuration = configuration.Value; - _identityService = identityService; - _cryptographicImplementation = cryptographicImplementation; - _eventDispatcher = eventDispatcher; - _providerManager = providerManager; - - _managedSecrets = managedSecretStore; - _resources = resourceStore; - _managedSecretViewModel = managedSecretViewModelDelegate; - } - - public async Task Create(ManagedSecretViewModel inputSecret, CancellationToken cancellationToken) - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.SecretAdmin)) return new UnauthorizedResult(); - - var resources = await _resources.Get(cancellationToken); - var resourceIds = inputSecret.ResourceIds.Split(';').Select(r => Guid.Parse(r)).ToList(); - if (resourceIds.Any(id => !resources.Any(r => r.ObjectId == id))) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ManagedSecretsService.Create), "New Managed Secret attempted to use one or more invalid Resource IDs"); - return new NotFoundObjectResult("One or more Resource IDs not found!"); - } - - ManagedSecret newManagedSecret = new ManagedSecret() - { - Name = inputSecret.Name, - Description = inputSecret.Description, - ValidPeriod = TimeSpan.FromMinutes(inputSecret.ValidPeriodMinutes), - LastChanged = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(inputSecret.ValidPeriodMinutes), - TaskConfirmationStrategies = inputSecret.TaskConfirmationStrategies, - ResourceIds = resourceIds, - Nonce = await _cryptographicImplementation.GenerateCryptographicallyRandomString(_configuration.DefaultNonceLength) - }; - - await _managedSecrets.Create(newManagedSecret, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.SecretCreated, nameof(ManagedSecretsService.Create), newManagedSecret); - - return new OkObjectResult(_managedSecretViewModel(newManagedSecret)); - } - - public async Task List(HttpRequest req, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - return new OkObjectResult((await _managedSecrets.Get(cancellationToken)).Select(s => _managedSecretViewModel(s))); - } - - public async Task Get(HttpRequest req, Guid secretId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - if (!await _managedSecrets.ContainsId(secretId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ManagedSecretsService.Get), "Secret ID not found"); - return new NotFoundObjectResult("Secret not found!"); - } - - return new OkObjectResult(_managedSecretViewModel(await _managedSecrets.GetOne(secretId, cancellationToken))); - } - - public async Task Delete(HttpRequest req, Guid secretId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.SecretAdmin)) return new UnauthorizedResult(); - - if (!await _managedSecrets.ContainsId(secretId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ManagedSecretsService.Delete), "Secret ID not found"); - return new NotFoundObjectResult("Secret not found!"); - } - - await _managedSecrets.Delete(secretId, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.SecretDeleted, nameof(ManagedSecretsService.Delete), secretId); - - return new OkResult(); - } - - public async Task Update(ManagedSecretViewModel inputSecret, Guid secretId, CancellationToken cancellationToken) - { - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.SecretAdmin)) return new UnauthorizedResult(); - - if (!await _managedSecrets.ContainsId(secretId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ManagedSecretsService.Update), "Secret ID not found"); - return new NotFoundObjectResult("Secret not found!"); - } - - var resources = await _resources.Get(cancellationToken); - var resourceIds = inputSecret.ResourceIds.Split(';').Select(r => Guid.Parse(r)).ToList(); - if (resourceIds.Any(id => !resources.Any(r => r.ObjectId == id))) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ManagedSecretsService.Update), "New Managed Secret attempted to use one or more invalid Resource IDs"); - return new NotFoundObjectResult("One or more Resource IDs not found!"); - } - - ManagedSecret newManagedSecret = new ManagedSecret() - { - ObjectId = secretId, - Name = inputSecret.Name, - Description = inputSecret.Description, - ValidPeriod = TimeSpan.FromMinutes(inputSecret.ValidPeriodMinutes), - TaskConfirmationStrategies = inputSecret.TaskConfirmationStrategies, - ResourceIds = resourceIds, - Nonce = await _cryptographicImplementation.GenerateCryptographicallyRandomString(_configuration.DefaultNonceLength) - }; - - await _managedSecrets.Update(newManagedSecret, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.SecretUpdated, nameof(ManagedSecretsService.Update), newManagedSecret); - - return new OkObjectResult(_managedSecretViewModel(newManagedSecret)); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Services/ProvidersService.cs b/src/AuthJanitor.Functions.AdminApi/Services/ProvidersService.cs deleted file mode 100644 index ad2e0f7..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/ProvidersService.cs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared; -using AuthJanitor.UI.Shared.ViewModels; -using AuthJanitor.EventSinks; -using AuthJanitor.IdentityServices; -using AuthJanitor.Providers; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Linq; -using System.Threading.Tasks; -using System.Web.Http; -using AuthJanitor.Providers.Capabilities; -using AuthJanitor.ViewModels; -using Microsoft.Extensions.Logging; - -namespace AuthJanitor.Services -{ - /// - /// API functions to describe the loaded Providers and their configurations. - /// A Provider is a library containing logic to either rekey an object/service or manage the lifecycle of an application. - /// - public class ProvidersService - { - private readonly IIdentityService _identityService; - private readonly ILogger _logger; - private readonly EventDispatcherService _eventDispatcher; - private readonly ProviderManagerService _providerManager; - - private readonly Func _configViewModel; - private readonly Func _providerViewModel; - - public ProvidersService( - IIdentityService identityService, - ILogger logger, - EventDispatcherService eventDispatcher, - ProviderManagerService providerManager, - Func configViewModelDelegate, - Func providerViewModelDelegate) - { - _identityService = identityService; - _logger = logger; - _eventDispatcher = eventDispatcher; - _providerManager = providerManager; - - _configViewModel = configViewModelDelegate; - _providerViewModel = providerViewModelDelegate; - } - - public IActionResult List(HttpRequest req) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - return new OkObjectResult(_providerManager.LoadedProviders.Select(p => _providerViewModel(p))); - } - - public async Task Enumerate(HttpRequest req) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - _logger.LogInformation("Acquiring OBO token"); - var token = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync(); - - _logger.LogInformation("Starting provider enumeration"); - var providerSuggestions = await _providerManager.EnumerateProviders(token); - - _logger.LogInformation("Enumerated {Count} provider suggestions", providerSuggestions.Count()); - - var results = new System.Collections.Generic.List(); - return new OkObjectResult(providerSuggestions.Select(p => - { - try - { - return GetSuggestionViewModel(p); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error preparing suggestion: " + ex.ToString()); - return null; - } - }).Where(s => s != null)); - } - private ProviderResourceSuggestionViewModel GetSuggestionViewModel(ProviderResourceSuggestion p) => - new ProviderResourceSuggestionViewModel() - { - Name = p.Name, - ProviderType = p.ProviderType, - ProviderConfiguration = _configViewModel(p.Configuration), - ProviderConfigurationSerialized = p.SerializedConfiguration, - ResourcesAddressingThis = p.ResourcesAddressingThis.Select(r => GetSuggestionViewModel(r)) - }; - - public async Task GetBlankConfiguration(HttpRequest req, string providerType) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - var provider = _providerManager.LoadedProviders.FirstOrDefault(p => p.ProviderTypeName == providerType); - if (provider == null) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ProvidersService.GetBlankConfiguration), "Invalid Provider specified"); - return new NotFoundResult(); - } - return new OkObjectResult(_configViewModel(_providerManager.GetProviderConfiguration(provider.ProviderTypeName))); - } - - public async Task TestConfiguration( - string providerConfiguration, - HttpRequest req, - string providerType, - string testContext) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - Enum.TryParse(testContext, true, out TestAsContexts testContextEnum); - AccessTokenCredential credential = null; - try - { - switch (testContextEnum) - { - case TestAsContexts.AsApp: - credential = await _identityService.GetAccessTokenForApplicationAsync(); - break; - case TestAsContexts.AsUser: - credential = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync(); - break; - default: - return new BadRequestErrorMessageResult("Invalid test context"); - } - if (credential == null || string.IsNullOrEmpty(credential.AccessToken)) - throw new Exception("Credential was empty!"); - } - catch (Exception ex) - { - return new BadRequestErrorMessageResult( - "Error retrieving Access Token: " + Environment.NewLine + - ex.Message + Environment.NewLine + - ex.StackTrace); - } - - var provider = _providerManager.LoadedProviders.FirstOrDefault(p => p.ProviderTypeName == providerType); - if (provider == null) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ProvidersService.GetBlankConfiguration), "Invalid Provider specified"); - return new NotFoundResult(); - } - - if (typeof(ICanRunSanityTests).IsAssignableFrom(provider.ProviderType)) - { - try - { - var instance = _providerManager.GetProviderInstance(provider.ProviderTypeName, providerConfiguration); - if (instance == null) - return new BadRequestErrorMessageResult("Provider configuration is invalid!"); - instance.Credential = credential; - await (instance as ICanRunSanityTests).Test(); - } - catch (Exception ex) - { - return new BadRequestErrorMessageResult(ex.Message); - } - } - else - return new BadRequestErrorMessageResult("Provider does not support testing!"); - - return new OkResult(); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Services/RekeyingTasksService.cs b/src/AuthJanitor.Functions.AdminApi/Services/RekeyingTasksService.cs deleted file mode 100644 index f2e9109..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/RekeyingTasksService.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.EventSinks; -using AuthJanitor.IdentityServices; -using AuthJanitor.UI.Shared.Models; -using AuthJanitor.UI.Shared.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Web.Http; - -namespace AuthJanitor.Services -{ - /// - /// API functions to control the creation management, and approval of Rekeying Tasks. - /// A Rekeying Task is a time-bounded description of one or more Managed Secrets to be rekeyed. - /// - public class RekeyingTasksService - { - private readonly AuthJanitorCoreConfiguration _configuration; - private readonly IIdentityService _identityService; - private readonly TaskExecutionMetaService _taskExecutionMetaService; - private readonly ProviderManagerService _providerManager; - private readonly EventDispatcherService _eventDispatcher; - - private readonly IDataStore _managedSecrets; - private readonly IDataStore _rekeyingTasks; - private readonly Func _rekeyingTaskViewModel; - - public RekeyingTasksService( - IOptions configuration, - IIdentityService identityService, - TaskExecutionMetaService taskExecutionMetaService, - EventDispatcherService eventDispatcher, - ProviderManagerService providerManager, - IDataStore managedSecretStore, - IDataStore rekeyingTaskStore, - Func rekeyingTaskViewModelDelegate) - { - _configuration = configuration.Value; - _identityService = identityService; - _taskExecutionMetaService = taskExecutionMetaService; - _eventDispatcher = eventDispatcher; - _providerManager = providerManager; - - _managedSecrets = managedSecretStore; - _rekeyingTasks = rekeyingTaskStore; - _rekeyingTaskViewModel = rekeyingTaskViewModelDelegate; - } - - public async Task Create(HttpRequest req, Guid secretId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.ServiceOperator)) return new UnauthorizedResult(); - - if (!await _managedSecrets.ContainsId(secretId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(RekeyingTasksService.Create), "Secret ID not found"); - return new NotFoundObjectResult("Secret not found!"); - } - - var secret = await _managedSecrets.GetOne(secretId, cancellationToken); - if (!secret.TaskConfirmationStrategies.HasFlag(TaskConfirmationStrategies.AdminCachesSignOff) && - !secret.TaskConfirmationStrategies.HasFlag(TaskConfirmationStrategies.AdminSignsOffJustInTime)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(RekeyingTasksService.Create), "Managed Secret does not support adminstrator approval"); - return new BadRequestErrorMessageResult("Managed Secret does not support administrator approval!"); - } - - RekeyingTask newTask = new RekeyingTask() - { - Queued = DateTimeOffset.UtcNow, - Expiry = secret.Expiry, - ManagedSecretId = secret.ObjectId - }; - - await _rekeyingTasks.Create(newTask, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCreatedForApproval, nameof(RekeyingTasksService.Create), newTask); - - return new OkObjectResult(newTask); - } - - public async Task List(HttpRequest req, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - return new OkObjectResult((await _rekeyingTasks.Get(cancellationToken)).Select(t => _rekeyingTaskViewModel(t))); - } - - public async Task Get(HttpRequest req, Guid taskId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - if (!await _rekeyingTasks.ContainsId(taskId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(RekeyingTasksService.Get), "Rekeying Task not found"); - return new NotFoundResult(); - } - - return new OkObjectResult(_rekeyingTaskViewModel((await _rekeyingTasks.GetOne(taskId, cancellationToken)))); - } - - public async Task Delete(HttpRequest req, Guid taskId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.ServiceOperator)) return new UnauthorizedResult(); - - if (!await _rekeyingTasks.ContainsId(taskId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(RekeyingTasksService.Delete), "Rekeying Task not found"); - return new NotFoundResult(); - } - - await _rekeyingTasks.Delete(taskId, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.RotationTaskDeleted, nameof(RekeyingTasksService.Delete), taskId); - - return new OkResult(); - } - - public async Task Approve(HttpRequest req, Guid taskId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.ServiceOperator)) return new UnauthorizedResult(); - - var toRekey = await _rekeyingTasks.GetOne(t => t.ObjectId == taskId, cancellationToken); - if (toRekey == null) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(RekeyingTasksService.Delete), "Rekeying Task not found"); - return new NotFoundResult(); - } - if (!toRekey.ConfirmationType.UsesOBOTokens()) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(RekeyingTasksService.Approve), "Rekeying Task does not support Administrator approval"); - return new BadRequestErrorMessageResult("Task does not support Administrator approval"); - } - - // Just cache credentials if no workflow action is required - if (toRekey.ConfirmationType == TaskConfirmationStrategies.AdminCachesSignOff) - await _taskExecutionMetaService.CacheBackCredentialsForTaskIdAsync(toRekey.ObjectId, cancellationToken); - else - await _taskExecutionMetaService.ExecuteTask(toRekey.ObjectId, cancellationToken); - - return new OkResult(); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Services/ResourcesService.cs b/src/AuthJanitor.Functions.AdminApi/Services/ResourcesService.cs deleted file mode 100644 index b1b5d27..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/ResourcesService.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.EventSinks; -using AuthJanitor.IdentityServices; -using AuthJanitor.UI.Shared.Models; -using AuthJanitor.UI.Shared.ViewModels; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Web.Http; - -namespace AuthJanitor.Services -{ - /// - /// API functions to control the creation and management of AuthJanitor Resources. - /// A Resource is the description of how to connect to an object or resource, using a given Provider. - /// - public class ResourcesService - { - private readonly IIdentityService _identityService; - private readonly ProviderManagerService _providerManager; - private readonly EventDispatcherService _eventDispatcher; - - private readonly IDataStore _resources; - private readonly Func _resourceViewModel; - - public ResourcesService( - IIdentityService identityService, - EventDispatcherService eventDispatcher, - ProviderManagerService providerManager, - IDataStore resourceStore, - Func resourceViewModelDelegate) - { - _identityService = identityService; - _eventDispatcher = eventDispatcher; - _providerManager = providerManager; - - _resources = resourceStore; - _resourceViewModel = resourceViewModelDelegate; - } - - public async Task Create(ResourceViewModel resource, HttpRequest req, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.ResourceAdmin)) return new UnauthorizedResult(); - - var provider = _providerManager.GetProviderMetadata(resource.ProviderType); - if (provider == null) - return new NotFoundObjectResult("Provider type not found"); - - if (!_providerManager.TestProviderConfiguration(provider.ProviderTypeName, resource.SerializedProviderConfiguration)) - return new BadRequestErrorMessageResult("Invalid Provider configuration!"); - - Resource newResource = new Resource() - { - Name = resource.Name, - Description = resource.Description, - ProviderType = provider.ProviderTypeName, - ProviderConfiguration = resource.SerializedProviderConfiguration - }; - - await _resources.Create(newResource, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.ResourceCreated, nameof(ResourcesService.Create), newResource); - - return new OkObjectResult(_resourceViewModel(newResource)); - } - - public async Task List(HttpRequest req, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - return new OkObjectResult((await _resources.Get(cancellationToken)).Select(r => _resourceViewModel(r))); - } - - public async Task Get(HttpRequest req, Guid resourceId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - if (!await _resources.ContainsId(resourceId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ResourcesService.Get), "Resource not found"); - return new NotFoundResult(); - } - - return new OkObjectResult(_resourceViewModel(await _resources.GetOne(resourceId, cancellationToken))); - } - - public async Task Delete(HttpRequest req, Guid resourceId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.ResourceAdmin)) return new UnauthorizedResult(); - - if (!await _resources.ContainsId(resourceId, cancellationToken)) - { - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.AnomalousEventOccurred, nameof(ResourcesService.Delete), "Resource not found"); - return new NotFoundResult(); - } - - await _resources.Delete(resourceId, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.ResourceDeleted, nameof(ResourcesService.Delete), resourceId); - - return new OkResult(); - } - - public async Task Update(ResourceViewModel resource, HttpRequest req, Guid resourceId, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.CurrentUserHasRole(AuthJanitorRoles.ResourceAdmin)) return new UnauthorizedResult(); - - var provider = _providerManager.GetProviderMetadata(resource.ProviderType); - if (provider == null) - return new NotFoundObjectResult("Provider type not found"); - - if (!_providerManager.TestProviderConfiguration(provider.ProviderTypeName, resource.SerializedProviderConfiguration)) - return new BadRequestErrorMessageResult("Invalid Provider configuration!"); - - Resource newResource = new Resource() - { - ObjectId = resourceId, - Name = resource.Name, - Description = resource.Description, - ProviderType = resource.ProviderType, - ProviderConfiguration = resource.SerializedProviderConfiguration - }; - - await _resources.Update(newResource, cancellationToken); - - await _eventDispatcher.DispatchEvent(AuthJanitorSystemEvents.ResourceUpdated, nameof(ResourcesService.Update), newResource); - - return new OkObjectResult(_resourceViewModel(newResource)); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Services/ScheduleRekeyingTasksService.cs b/src/AuthJanitor.Functions.AdminApi/Services/ScheduleRekeyingTasksService.cs deleted file mode 100644 index c1cc235..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/ScheduleRekeyingTasksService.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.EventSinks; -using AuthJanitor.UI.Shared.Models; -using Microsoft.Azure.WebJobs; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Services -{ - public class ScheduleRekeyingTasksService - { - private readonly AuthJanitorCoreConfiguration _configuration; - private readonly ProviderManagerService _providerManager; - private readonly EventDispatcherService _eventDispatcherMetaService; - - private readonly IDataStore _managedSecrets; - private readonly IDataStore _resources; - private readonly IDataStore _rekeyingTasks; - - public ScheduleRekeyingTasksService( - IOptions configuration, - EventDispatcherService eventDispatcherMetaService, - ProviderManagerService providerManager, - IDataStore managedSecretStore, - IDataStore resourceStore, - IDataStore rekeyingTaskStore) - { - _configuration = configuration.Value; - _eventDispatcherMetaService = eventDispatcherMetaService; - _providerManager = providerManager; - - _managedSecrets = managedSecretStore; - _resources = resourceStore; - _rekeyingTasks = rekeyingTaskStore; - } - - public async Task Run(TimerInfo myTimer, ILogger log, CancellationToken cancellationToken) - { - _ = myTimer; // unused but required for attribute - - log.LogInformation($"Scheduling Rekeying Tasks for upcoming ManagedSecret expirations"); - - await ScheduleApprovalRequiredTasks(log, cancellationToken); - await ScheduleAutoRekeyingTasks(log, cancellationToken); - } - - public async Task ScheduleApprovalRequiredTasks(ILogger log, CancellationToken cancellationToken) - { - var jitCandidates = await GetSecretsForRekeyingTask( - TaskConfirmationStrategies.AdminSignsOffJustInTime, - _configuration.ApprovalRequiredLeadTimeHours, cancellationToken); - log.LogInformation("Creating {TaskCount} tasks for just-in-time administrator approval", jitCandidates.Count); - await CreateAndNotify(jitCandidates.Select(s => CreateRekeyingTask(s, s.Expiry)), cancellationToken); - - // TODO: Implement schedule of availability windows and adjust timing here to match... - // ... e.g. if a ManagedSecret expires on a Thursday but schedule only allows key changes - // on weekends, expiry needs to be shifted backwards to the weekend prior to the Thursday - // expiry. - var cachedCandidates = await GetSecretsForRekeyingTask( - TaskConfirmationStrategies.AdminCachesSignOff, - _configuration.ApprovalRequiredLeadTimeHours, cancellationToken); - log.LogInformation("Creating {TaskCount} tasks for cached administrator approval", cachedCandidates.Count); - await CreateAndNotify(cachedCandidates.Select(s => CreateRekeyingTask(s, s.Expiry)), cancellationToken); - } - - public async Task ScheduleAutoRekeyingTasks(ILogger log, CancellationToken cancellationToken) - { - var jitCandidates = await GetSecretsForRekeyingTask( - TaskConfirmationStrategies.AutomaticRekeyingAsNeeded, - _configuration.AutomaticRekeyableTaskCreationLeadTimeHours, cancellationToken); - log.LogInformation("Creating {TaskCount} tasks for just-in-time auto-rekeying", jitCandidates.Count); - await CreateAndNotify(jitCandidates.Select(s => CreateRekeyingTask(s, s.Expiry)), cancellationToken); - - // TODO: Implement schedule of availability windows and adjust timing here to match... - // ... e.g. if a ManagedSecret expires on a Thursday but schedule only allows key changes - // on weekends, expiry needs to be shifted backwards to the weekend prior to the Thursday - // expiry. - var cachedCandidates = await GetSecretsForRekeyingTask( - TaskConfirmationStrategies.AutomaticRekeyingScheduled, - _configuration.AutomaticRekeyableTaskCreationLeadTimeHours, cancellationToken); - log.LogInformation("Creating {TaskCount} tasks for scheduled auto-rekeying", cachedCandidates.Count); - await CreateAndNotify(cachedCandidates.Select(s => CreateRekeyingTask(s, s.Expiry)), cancellationToken); - } - - private RekeyingTask CreateRekeyingTask(ManagedSecret secret, DateTimeOffset expiry) => - new RekeyingTask() - { - ManagedSecretId = secret.ObjectId, - Expiry = expiry, - ConfirmationType = GetPreferredConfirmation(secret.TaskConfirmationStrategies), - Queued = DateTimeOffset.UtcNow, - RekeyingInProgress = false - }; - - private async Task CreateAndNotify(IEnumerable tasks, CancellationToken cancellationToken) - { - if (!tasks.Any()) return; - await Task.WhenAll(tasks.Select(t => _rekeyingTasks.Create(t, cancellationToken))); - - foreach (var task in tasks) - { - var secret = await _managedSecrets.GetOne(task.ManagedSecretId, cancellationToken); - if (task.ConfirmationType.UsesOBOTokens()) - await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCreatedForApproval, nameof(ScheduleRekeyingTasksService.CreateAndNotify), task); - else - await _eventDispatcherMetaService.DispatchEvent(AuthJanitorSystemEvents.RotationTaskCreatedForAutomation, nameof(ScheduleRekeyingTasksService.CreateAndNotify), task); - } - } - - private TaskConfirmationStrategies GetPreferredConfirmation(TaskConfirmationStrategies taskConfirmationStrategy) - { - if (taskConfirmationStrategy.HasFlag(TaskConfirmationStrategies.AdminCachesSignOff) && - taskConfirmationStrategy.HasFlag(TaskConfirmationStrategies.AdminSignsOffJustInTime)) - return TaskConfirmationStrategies.AdminCachesSignOff; - if (taskConfirmationStrategy.HasFlag(TaskConfirmationStrategies.AutomaticRekeyingAsNeeded) && - taskConfirmationStrategy.HasFlag(TaskConfirmationStrategies.AutomaticRekeyingScheduled)) - return TaskConfirmationStrategies.AutomaticRekeyingScheduled; - return taskConfirmationStrategy; - } - - private async Task> GetSecretsForRekeyingTask( - TaskConfirmationStrategies taskConfirmationStrategies, - int leadTimeHours, - CancellationToken cancellationToken) - { - var secretsToRotate = await _managedSecrets.Get(s => - s.TaskConfirmationStrategies.HasFlag(taskConfirmationStrategies) && - s.Expiry < DateTimeOffset.UtcNow + TimeSpan.FromHours(leadTimeHours), cancellationToken); - - var rekeyingTasks = await _rekeyingTasks.Get(cancellationToken); - return secretsToRotate - .Where(s => !rekeyingTasks.Any(t => - t.ManagedSecretId == s.ObjectId && - !t.RekeyingCompleted)) - .ToList(); - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.AdminApi/Services/SystemService.cs b/src/AuthJanitor.Functions.AdminApi/Services/SystemService.cs deleted file mode 100644 index 3e30c04..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Services/SystemService.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.IdentityServices; -using AuthJanitor.Integrity; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Services -{ - public class SystemService - { - private readonly IIdentityService _identityService; - private readonly SystemIntegrityService _systemIntegrityService; - - public SystemService( - IIdentityService identityService, - SystemIntegrityService systemIntegrityService) - { - _identityService = identityService; - _systemIntegrityService = systemIntegrityService; - } - - public async Task GetIntegrityReports(HttpRequest req, CancellationToken cancellationToken) - { - _ = req; - - if (!_identityService.IsUserLoggedIn) return new UnauthorizedResult(); - - try - { - var reports = await _systemIntegrityService.GetIntegrityReports(); - return new OkObjectResult(reports); - } - catch (Exception) - { - return new BadRequestResult(); - } - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/Startup.cs b/src/AuthJanitor.Functions.AdminApi/Startup.cs deleted file mode 100644 index 1baab2c..0000000 --- a/src/AuthJanitor.Functions.AdminApi/Startup.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared; -using AuthJanitor.Integrations.CryptographicImplementations.Default; -using AuthJanitor.Integrations.DataStores.AzureBlobStorage; -using AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory; -using AuthJanitor.Integrations.SecureStorage.AzureKeyVault; -using AuthJanitor.Providers; -using McMaster.NETCore.Plugins; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using AuthJanitor.Services; - -[assembly: WebJobsStartup(typeof(AuthJanitor.Startup))] -namespace AuthJanitor -{ - public class Startup : IWebJobsStartup, IAuthJanitorIntegrityIncluded - { - private const string PROVIDER_SEARCH_MASK = "AuthJanitor.Providers.*.dll"; - private static readonly string PROVIDER_SEARCH_PATH = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..")); - private static readonly Type[] PROVIDER_SHARED_TYPES = new Type[] - { - typeof(IAuthJanitorProvider), - typeof(AuthJanitorProvider<>), - typeof(IServiceCollection), - typeof(ILogger) - }; - - public static ILogger REMOVE_ME_LOGGER; - - public void Configure(IWebJobsBuilder builder) - { - var logger = LoggerFactory.Create(builder => - { - builder.SetMinimumLevel(LogLevel.Debug) - .AddConsole(); - }).CreateLogger(); - REMOVE_ME_LOGGER = logger; - - builder.Services.AddOptions(); - - builder.Services.AddHttpContextAccessor(); - - logger.LogDebug("Registering Azure AD Identity Service"); - builder.Services.AddAJAzureActiveDirectory(o => - { - o.ClientId = Environment.GetEnvironmentVariable("CLIENT_ID", EnvironmentVariableTarget.Process); - o.ClientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET", EnvironmentVariableTarget.Process); - o.TenantId = Environment.GetEnvironmentVariable("TENANT_ID", EnvironmentVariableTarget.Process); - }); - builder.Services.AddAJAzureActiveDirectoryManager(o => - { - o.ClientId = Environment.GetEnvironmentVariable("CLIENT_ID", EnvironmentVariableTarget.Process); - o.ClientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET", EnvironmentVariableTarget.Process); - o.TenantId = Environment.GetEnvironmentVariable("TENANT_ID", EnvironmentVariableTarget.Process); - }); - - logger.LogDebug("Registering Event Sinks"); - - // TODO: Register IEventSinks here, before the EventDispatcherService - // This is where we offload to Azure Sentinel, send emails, etc. - // The *entire system* offloads to the EventDispatcherService to generalize events. - - logger.LogDebug("Registering Cryptographic Implementation"); - builder.Services.AddAJDefaultCryptographicImplementation(o => - { - o.PublicKey = new byte[0]; - o.PrivateKey = new byte[0]; - }); - - logger.LogDebug("Registering Secure Storage Provider"); - builder.Services.AddAJAzureKeyVault(o => - { - o.VaultName = "vault"; - }); - - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - - // ----- - - logger.LogDebug("Registering DataStores"); - builder.Services.AddAJAzureBlobStorage(o => - { - o.ConnectionString = Environment.GetEnvironmentVariable("AzureWebJobsStorage", EnvironmentVariableTarget.Process); - o.Container = "authjanitor"; - }); - - // ----- - - logger.LogDebug("Registering ViewModel generators"); - ViewModelFactory.ConfigureServices(builder.Services); - - // ----- - - logger.LogDebug("Scanning for Provider modules at {ProviderSearchPath}\\{ProviderSearchMask} recursively", PROVIDER_SEARCH_PATH, PROVIDER_SEARCH_MASK); - - var providerTypes = Directory.GetFiles(PROVIDER_SEARCH_PATH, PROVIDER_SEARCH_MASK, new EnumerationOptions() { RecurseSubdirectories = true }) - .SelectMany(libraryFile => PluginLoader.CreateFromAssemblyFile(libraryFile, PROVIDER_SHARED_TYPES) - .LoadDefaultAssembly() - .GetTypes() - .Where(type => !type.IsAbstract && typeof(IAuthJanitorProvider).IsAssignableFrom(type))) - .ToArray(); - - logger.LogInformation("Found {ProviderCount} providers: {ProviderTypeNames}", providerTypes.Length, string.Join(" ", providerTypes.Select(t => t.Name))); - logger.LogInformation("Registering Provider Manager Service"); - builder.Services.AddAuthJanitorService("admin-service", providerTypes); - } - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/host.json b/src/AuthJanitor.Functions.AdminApi/host.json deleted file mode 100644 index ba2a2e8..0000000 --- a/src/AuthJanitor.Functions.AdminApi/host.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "2.0", - "functionTimeout": "00:10:00", - "extensions": { - "http": { - "routePrefix": "api", - "maxOutstandingRequests": 200, - "maxConcurrentRequests": 100, - "dynamicThrottlesEnabled": true - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.AdminApi/local.settings.json b/src/AuthJanitor.Functions.AdminApi/local.settings.json deleted file mode 100644 index 8769c85..0000000 --- a/src/AuthJanitor.Functions.AdminApi/local.settings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "dotnet", - "AZURE_FUNCTION_PROXY_DISABLE_LOCAL_CALL": true, - "STORAGE_WEB_URL": "http://localhost:16100", - "CLIENT_ID": "authjanitor-admin-api-app-client-id", - "CLIENT_SECRET": "authjanitor-admin-api-app-client-secret", - "TENANT_ID": "tenant-being-maintained-by-authjanitor" - } -} diff --git a/src/AuthJanitor.Functions.AdminApi/proxies.json b/src/AuthJanitor.Functions.AdminApi/proxies.json deleted file mode 100644 index 5b2fe31..0000000 --- a/src/AuthJanitor.Functions.AdminApi/proxies.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/proxies", - "proxies": { - "ui-index": { - "matchCondition": { "route": "/" }, - "backendUri": "%STORAGE_WEB_URL%/index.html" - }, - "ui-index-explicit": { - "matchCondition": { "route": "/index.html" }, - "backendUri": "%STORAGE_WEB_URL%/index.html" - }, - "ui-dashboard": { - "matchCondition": { "route": "/dashboard" }, - "backendUri": "%STORAGE_WEB_URL%/dashboard" - }, - "ui-providers": { - "matchCondition": { "route": "/providers" }, - "backendUri": "%STORAGE_WEB_URL%/providers" - }, - "ui-providers-suggestions": { - "matchCondition": { "route": "/providers/suggestions" }, - "backendUri": "%STORAGE_WEB_URL%/providers/suggestions" - }, - "ui-integrityReports": { - "matchCondition": { "route": "/integrityReports" }, - "backendUri": "%STORAGE_WEB_URL%/integrityReports" - }, - "ui-accessManagement": { - "matchCondition": { "route": "/accessManagement" }, - "backendUri": "%STORAGE_WEB_URL%/accessManagement" - }, - - "ui-managedSecrets": { - "matchCondition": { "route": "/managedSecrets" }, - "backendUri": "%STORAGE_WEB_URL%/managedSecrets" - }, - "ui-managedSecrets-detail": { - "matchCondition": { "route": "/managedSecrets/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/managedSecrets/{restOfPath}" - }, - - "ui-rekeyingTasks": { - "matchCondition": { "route": "/rekeyingTasks" }, - "backendUri": "%STORAGE_WEB_URL%/rekeyingTasks" - }, - "ui-rekeyingTasks-detail": { - "matchCondition": { "route": "/rekeyingTasks/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/rekeyingTasks/{restOfPath}" - }, - - "ui-resources": { - "matchCondition": { "route": "/resources" }, - "backendUri": "%STORAGE_WEB_URL%/resources" - }, - "ui-resources-detail": { - "matchCondition": { "route": "/resources/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/resources/{restOfPath}" - }, - - "ui-dependency-favicon": { - "matchCondition": { "route": "/favicon.ico" }, - "backendUri": "%STORAGE_WEB_URL%/favicon.ico" - }, - "ui-dependency-browserconfig": { - "matchCondition": { "route": "/browserconfig.xml" }, - "backendUri": "%STORAGE_WEB_URL%/browserconfig.xml" - }, - "ui-dependency-css": { - "matchCondition": { "route": "/css/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/css/{restOfPath}" - }, - "ui-dependency-icons": { - "matchCondition": { "route": "/icons/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/icons/{restOfPath}" - }, - "ui-dependency-content": { - "matchCondition": { "route": "/_content/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/_content/{restOfPath}" - }, - "ui-dependency-framework": { - "matchCondition": { "route": "/_framework/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/_framework/{restOfPath}" - }, - "ui-dependency-js": { - "matchCondition": { "route": "/js/{*restOfPath}" }, - "backendUri": "%STORAGE_WEB_URL%/js/{restOfPath}" - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.Agent/.gitignore b/src/AuthJanitor.Functions.Agent/.gitignore deleted file mode 100644 index ff5b00c..0000000 --- a/src/AuthJanitor.Functions.Agent/.gitignore +++ /dev/null @@ -1,264 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# Azure Functions localsettings file -local.settings.json - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc \ No newline at end of file diff --git a/src/AuthJanitor.Functions.Agent/AuthJanitor.Functions.Agent.csproj b/src/AuthJanitor.Functions.Agent/AuthJanitor.Functions.Agent.csproj deleted file mode 100644 index 324320e..0000000 --- a/src/AuthJanitor.Functions.Agent/AuthJanitor.Functions.Agent.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - netcoreapp3.1 - v3 - Debug;Release - AuthJanitor - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - Never - - - \ No newline at end of file diff --git a/src/AuthJanitor.Functions.Agent/ExternalSignal.cs b/src/AuthJanitor.Functions.Agent/ExternalSignal.cs deleted file mode 100644 index 00b6c9e..0000000 --- a/src/AuthJanitor.Functions.Agent/ExternalSignal.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.UI.Shared.Models; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Web.Http; - -namespace AuthJanitor -{ - public class ExternalSignal - { - private const int RETURN_NO_CHANGE = 0; - private const int RETURN_CHANGE_OCCURRED = 1; - private const int RETURN_RETRY_SHORTLY = 2; - - private const int MAX_EXECUTION_SECONDS_BEFORE_RETRY = 30; - - private readonly AuthJanitorCoreConfiguration _configuration; - private readonly TaskExecutionMetaService _taskExecutionMetaService; - - private readonly IDataStore _managedSecrets; - private readonly IDataStore _rekeyingTasks; - - public ExternalSignal( - IOptions configuration, - TaskExecutionMetaService taskExecutionMetaService, - IDataStore managedSecretStore, - IDataStore rekeyingTaskStore) - { - _configuration = configuration.Value; - _taskExecutionMetaService = taskExecutionMetaService; - - _managedSecrets = managedSecretStore; - _rekeyingTasks = rekeyingTaskStore; - } - - [FunctionName("ExternalSignal")] - public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "secrets/{managedSecretId:guid}/{nonce}")] HttpRequest req, - Guid managedSecretId, - string nonce, - ILogger log, - CancellationToken cancellationToken) - { - _ = req; // unused but required for attribute - - log.LogInformation("External signal called to check ManagedSecret ID {ManagedSecretId} against nonce {Nonce}", managedSecretId, nonce); - - var secret = await _managedSecrets.GetOne(managedSecretId, cancellationToken); - if (secret == null) - return new BadRequestErrorMessageResult("Invalid ManagedSecret ID"); - if (!secret.TaskConfirmationStrategies.HasFlag(TaskConfirmationStrategies.ExternalSignal)) - return new BadRequestErrorMessageResult("This ManagedSecret cannot be used with External Signals"); - - if ((await _rekeyingTasks.Get(t => t.ManagedSecretId == secret.ObjectId, cancellationToken)) - .Any(t => t.RekeyingInProgress)) - { - return new OkObjectResult(RETURN_RETRY_SHORTLY); - } - - if ((secret.IsValid && secret.TimeRemaining <= TimeSpan.FromHours(_configuration.ExternalSignalRekeyableLeadTimeHours)) || !secret.IsValid) - { - var executeRekeyingTask = Task.Run(async () => - { - var rekeyingTask = new RekeyingTask() - { - ManagedSecretId = secret.ObjectId, - Expiry = secret.Expiry, - Queued = DateTimeOffset.UtcNow, - RekeyingInProgress = true - }; - - await _rekeyingTasks.Create(rekeyingTask, cancellationToken).ConfigureAwait(false); - - await _taskExecutionMetaService.ExecuteTask(rekeyingTask.ObjectId, cancellationToken).ConfigureAwait(false); - }); - - var timeout = TimeSpan.FromSeconds(MAX_EXECUTION_SECONDS_BEFORE_RETRY); - var timeoutCancellationTokenSource = new CancellationTokenSource(); - var timeoutTask = Task.Delay(timeout, timeoutCancellationTokenSource.Token); - - var completedTask = await Task.WhenAny(executeRekeyingTask, timeoutTask); - - // If the task that completed first was the timeout task we need to let the caller know it's still running - if (completedTask == timeoutTask) - { - log.LogInformation("Rekeying workflow was started but exceeded the maximum request time! ({MaxExecutionRequestTime})", timeout); - return new OkObjectResult(RETURN_RETRY_SHORTLY); - } - else - { - // Signal that the timeout task can be canceled - timeoutCancellationTokenSource.Cancel(); - - // The rekeying task completed in time, let the caller know - log.LogInformation("Completed rekeying workflow within maximum time! ({MaxExecutionRequestTime})", timeout); - return new OkObjectResult(RETURN_CHANGE_OCCURRED); - } - } - return new OkObjectResult(RETURN_NO_CHANGE); - } - } -} diff --git a/src/AuthJanitor.Functions.Agent/PerformAutoRekeyingTasks.cs b/src/AuthJanitor.Functions.Agent/PerformAutoRekeyingTasks.cs deleted file mode 100644 index ccf66ac..0000000 --- a/src/AuthJanitor.Functions.Agent/PerformAutoRekeyingTasks.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -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 -{ - public class PerformAutoRekeyingTasks - { - private readonly AuthJanitorCoreConfiguration _configuration; - private readonly TaskExecutionMetaService _taskExecutionMetaService; - - private readonly IDataStore _rekeyingTasks; - - public PerformAutoRekeyingTasks( - IOptions configuration, - TaskExecutionMetaService taskExecutionMetaService, - IDataStore rekeyingTaskStore) - { - _configuration = configuration.Value; - _taskExecutionMetaService = taskExecutionMetaService; - _rekeyingTasks = rekeyingTaskStore; - } - - [FunctionName("PerformAutoRekeyingTasks")] - public async Task Run([TimerTrigger("0 */2 * * * *")] TimerInfo myTimer, ILogger log, CancellationToken cancellationToken) - { - _ = myTimer; // unused but required for attribute - - log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}"); - - var toRekey = await _rekeyingTasks.Get(t => - (t.ConfirmationType == TaskConfirmationStrategies.AdminCachesSignOff || - t.ConfirmationType == TaskConfirmationStrategies.AutomaticRekeyingAsNeeded || - t.ConfirmationType == TaskConfirmationStrategies.AutomaticRekeyingScheduled) && - DateTimeOffset.UtcNow + TimeSpan.FromHours(_configuration.AutomaticRekeyableJustInTimeLeadTimeHours) > t.Expiry, cancellationToken); - - foreach (var task in toRekey) - { - await _taskExecutionMetaService.ExecuteTask(task.ObjectId, cancellationToken); - } - } - } -} diff --git a/src/AuthJanitor.Functions.Agent/Properties/launchSettings.json b/src/AuthJanitor.Functions.Agent/Properties/launchSettings.json deleted file mode 100644 index 49401ce..0000000 --- a/src/AuthJanitor.Functions.Agent/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "AuthJanitor.Functions.Agent": { - "commandName": "Project", - "commandLineArgs": "host start --pause-on-error --port 16020" - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.Agent/Startup.cs b/src/AuthJanitor.Functions.Agent/Startup.cs deleted file mode 100644 index 215d5c4..0000000 --- a/src/AuthJanitor.Functions.Agent/Startup.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared; -using AuthJanitor.Integrations.CryptographicImplementations.Default; -using AuthJanitor.Integrations.DataStores.AzureBlobStorage; -using AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory; -using AuthJanitor.Integrations.SecureStorage.AzureKeyVault; -using AuthJanitor.Providers; -using McMaster.NETCore.Plugins; -using Microsoft.Azure.Functions.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security.Cryptography; - -[assembly: FunctionsStartup(typeof(AuthJanitor.Startup))] -namespace AuthJanitor -{ - public class Startup : FunctionsStartup - { - private const string PROVIDER_SEARCH_MASK = "AuthJanitor.Providers.*.dll"; - private static readonly string PROVIDER_SEARCH_PATH = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..")); - private static readonly Type[] PROVIDER_SHARED_TYPES = new Type[] - { - typeof(IAuthJanitorProvider), - typeof(AuthJanitorProvider<>), - typeof(IServiceCollection), - typeof(ILogger) - }; - - public override void Configure(IFunctionsHostBuilder builder) - { - var logger = LoggerFactory.Create(builder => - { - builder.SetMinimumLevel(LogLevel.Debug) - .AddConsole(); - }).CreateLogger(); - - builder.Services.AddOptions(); - - logger.LogDebug("Registering Azure AD Identity Service"); - builder.Services.AddAJAzureActiveDirectory(o => - { - o.ClientId = "clientId"; - o.ClientSecret = "clientSecret"; - o.TenantId = "tenantId"; - }); - - logger.LogDebug("Registering Event Sinks"); - - // TODO: Register IEventSinks here, before the EventDispatcherService - // This is where we offload to Azure Sentinel, send emails, etc. - // The *entire system* offloads to the EventDispatcherService to generalize events. - - logger.LogDebug("Registering Cryptographic Implementation"); - var rsa = RSA.Create(); - builder.Services.AddAJDefaultCryptographicImplementation(o => - { - o.PublicKey = rsa.ExportRSAPublicKey(); - o.PrivateKey = rsa.ExportRSAPrivateKey(); - }); - - logger.LogDebug("Registering Secure Storage Provider"); - builder.Services.AddAJAzureKeyVault(o => - { - o.VaultName = "vault"; - }); - - // ----- - - logger.LogDebug("Registering DataStores"); - builder.Services.AddAJAzureBlobStorage(o => - { - o.ConnectionString = Environment.GetEnvironmentVariable("AzureWebJobsStorage", EnvironmentVariableTarget.Process); - o.Container = "authjanitor"; - }); - - // ----- - - logger.LogDebug("Registering ViewModel generators"); - ViewModelFactory.ConfigureServices(builder.Services); - - // ----- - - logger.LogDebug("Scanning for Provider modules at {ProviderSearchPath}\\{ProviderSearchMask} recursively", PROVIDER_SEARCH_PATH, PROVIDER_SEARCH_MASK); - - var providerTypes = Directory.GetFiles(PROVIDER_SEARCH_PATH, PROVIDER_SEARCH_MASK, new EnumerationOptions() { RecurseSubdirectories = true }) - .SelectMany(libraryFile => PluginLoader.CreateFromAssemblyFile(libraryFile, PROVIDER_SHARED_TYPES) - .LoadDefaultAssembly() - .GetTypes() - .Where(type => !type.IsAbstract && typeof(IAuthJanitorProvider).IsAssignableFrom(type))) - .ToArray(); - - logger.LogInformation("Found {ProviderCount} providers: {ProviderTypeNames}", providerTypes.Length, string.Join(" ", providerTypes.Select(t => t.Name))); - logger.LogInformation("Registering AuthJanitor Service"); - builder.Services.AddAuthJanitorService("agent-abc123", providerTypes); // todo: change this to env var - } - } -} diff --git a/src/AuthJanitor.Functions.Agent/host.json b/src/AuthJanitor.Functions.Agent/host.json deleted file mode 100644 index b9f92c0..0000000 --- a/src/AuthJanitor.Functions.Agent/host.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "2.0" -} \ No newline at end of file diff --git a/src/AuthJanitor.Functions.Agent/local.settings.json b/src/AuthJanitor.Functions.Agent/local.settings.json deleted file mode 100644 index ea3c9ee..0000000 --- a/src/AuthJanitor.Functions.Agent/local.settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "dotnet" - } -} diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault.csproj b/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault.csproj index 106546b..87dafdc 100644 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault.csproj +++ b/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault.csproj @@ -11,7 +11,6 @@ - diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementation.cs b/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementation.cs index 0bcac03..6f28c3b 100644 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementation.cs +++ b/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementation.cs @@ -1,7 +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; using Microsoft.Extensions.Options; @@ -21,17 +20,17 @@ public class AzureKeyVaultCryptographicImplementation : ICryptographicImplementa private AzureKeyVaultCryptographicImplementationConfiguration Configuration { get; } private ICryptographicImplementation FallbackCryptography => Configuration.FallbackCryptography; - private readonly IIdentityService _identityService; + private readonly ITokenCredentialProvider _tokenProvider; /// /// The default AuthJanitor cryptographic implementation /// public AzureKeyVaultCryptographicImplementation( IOptions configuration, - IIdentityService identityService) + ITokenCredentialProvider tokenProvider) { Configuration = configuration.Value; - _identityService = identityService; + _tokenProvider = tokenProvider; } /// @@ -173,13 +172,13 @@ public async Task Encrypt(string key, byte[] plainText) => plainText)).Ciphertext; private Task GetClient() => - _identityService.GetAccessTokenForApplicationAsync() + _tokenProvider.GetToken(Configuration.VaultTokenSource, string.Empty) .ContinueWith(t => new CryptographyClient( new Uri(Configuration.KeyIdUri), ExistingTokenCredential.FromAccessToken(t.Result))); private Task GetClient(string key) => - _identityService.GetAccessTokenForApplicationAsync() + _tokenProvider.GetToken(Configuration.VaultTokenSource, string.Empty) .ContinueWith(async t => { var keyClient = new KeyClient(new Uri(Configuration.VaultUri), diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementationConfiguration.cs b/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementationConfiguration.cs index 28fd8be..09c4456 100644 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementationConfiguration.cs +++ b/src/AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault/AzureKeyVaultCryptographicImplementationConfiguration.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. using AuthJanitor.CryptographicImplementations; +using AuthJanitor.Providers; namespace AuthJanitor.Integrations.CryptographicImplementations.AzureKeyVault { @@ -10,5 +11,7 @@ public class AzureKeyVaultCryptographicImplementationConfiguration public string VaultUri { get; set; } public string KeyIdUri { get; set; } + + public TokenSources VaultTokenSource { get; set; } } } diff --git a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/AuthJanitor.Integrations.CryptographicImplementations.Default.csproj b/src/AuthJanitor.Integrations.CryptographicImplementations.Default/AuthJanitor.Integrations.CryptographicImplementations.Default.csproj deleted file mode 100644 index 0945248..0000000 --- a/src/AuthJanitor.Integrations.CryptographicImplementations.Default/AuthJanitor.Integrations.CryptographicImplementations.Default.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - netstandard2.1 - - - - - - - - - - - diff --git a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AuthJanitor.Integrations.DataStores.AzureBlobStorage.csproj b/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AuthJanitor.Integrations.DataStores.AzureBlobStorage.csproj deleted file mode 100644 index a2a6d93..0000000 --- a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AuthJanitor.Integrations.DataStores.AzureBlobStorage.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard2.1 - - - - - - - - - - - - diff --git a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AzureBlobStorageDataStore.cs b/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AzureBlobStorageDataStore.cs deleted file mode 100644 index 95a6748..0000000 --- a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AzureBlobStorageDataStore.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using Azure; -using Azure.Storage.Blobs; -using Azure.Storage.Blobs.Models; -using Azure.Storage.Blobs.Specialized; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Integrations.DataStores.AzureBlobStorage -{ - public class AzureBlobStorageDataStore : IDataStore where TStoredModel : IAuthJanitorModel - { - private bool _isInitialized = false; - private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions() - { - Converters = { new TimeSpanConverter() } - }; - - private AzureBlobStorageDataStoreConfiguration Configuration { get; } - - protected BlockBlobClient Blob { get; private set; } - - protected List CachedCollection { get; private set; } = new List(); - - public AzureBlobStorageDataStore( - IOptions configuration) - { - Configuration = configuration.Value; - } - - public async Task ContainsId(Guid objectId, CancellationToken cancellationToken) - { - await Cache(); - - return CachedCollection.Any(m => m.ObjectId == objectId); - } - - public async Task Create(TStoredModel model, CancellationToken cancellationToken) - { - await Cache(); - - if (CachedCollection.Any(m => m.ObjectId == model.ObjectId)) - throw new InvalidOperationException("Object ID collision!"); - - CachedCollection.Add(model); - await Commit(); - - return model; - } - - public async Task Delete(Guid objectId, CancellationToken cancellationToken) - { - await Cache(); - - if (!CachedCollection.Any(m => m.ObjectId == objectId)) - throw new KeyNotFoundException("Object ID not found!"); - - CachedCollection.RemoveAll(c => c.ObjectId == objectId); - await Commit(); - } - - public async Task> Get(CancellationToken cancellationToken) - { - await Cache(); - - return new List(CachedCollection); - } - - public async Task> Get(Func whereClause, CancellationToken cancellationToken) - { - await Cache(); - - return new List(CachedCollection.Where(whereClause)); - } - - public async Task GetOne(Guid objectId, CancellationToken cancellationToken) - { - await Cache(); - - return CachedCollection.FirstOrDefault(c => c.ObjectId == objectId); - } - - public async Task GetOne(Func whereClause, CancellationToken cancellationToken) - { - await Cache(); - - return CachedCollection.FirstOrDefault(whereClause); - } - - public async Task Update(TStoredModel model, CancellationToken cancellationToken) - { - await Cache(); - - if (!CachedCollection.Any(m => m.ObjectId == model.ObjectId)) - throw new KeyNotFoundException("Object ID not found!"); - - CachedCollection.RemoveAll(c => c.ObjectId == model.ObjectId); - CachedCollection.Add(model); - await Commit(); - - return model; - } - - private async Task Commit() - { - await InitializeIfRequired(); - - var startedAttempt = DateTimeOffset.UtcNow; - bool success = false; - while ((DateTimeOffset.UtcNow - startedAttempt) < TimeSpan.FromSeconds(5)) - { - try - { - var etag = (await Blob.GetPropertiesAsync())?.Value?.ETag; - using (var ms = new MemoryStream()) - { - var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(CachedCollection, - new Newtonsoft.Json.JsonSerializerSettings() { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All }); - //var serialized = JsonSerializer.Serialize(CachedCollection, _serializerOptions); - ms.Write(Encoding.UTF8.GetBytes(serialized)); - ms.Seek(0, SeekOrigin.Begin); - Blob.Upload(ms, conditions: new BlobRequestConditions() { IfMatch = etag }); - success = true; - } - } - catch (RequestFailedException) - { - } - catch (Exception ex) { throw ex; } - - if (success) - break; - - await Task.Delay(100); - } - } - - private async Task Cache() - { - await InitializeIfRequired(); - - var blobText = Blob.Download(); - using (var ms = new MemoryStream()) - { - blobText.Value.Content.CopyTo(ms); - ms.Seek(0, SeekOrigin.Begin); - var str = Encoding.UTF8.GetString(ms.ToArray()); - //CachedCollection = JsonSerializer.Deserialize>(str, _serializerOptions); - CachedCollection = Newtonsoft.Json.JsonConvert.DeserializeObject>(str, - new Newtonsoft.Json.JsonSerializerSettings() { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All }); - } - } - - private async Task InitializeIfRequired() - { - if (!_isInitialized) - { - var blobServiceClient = new BlobServiceClient(Configuration.ConnectionString); - - var containerClient = blobServiceClient.GetBlobContainerClient(Configuration.Container); - await containerClient.CreateIfNotExistsAsync(); - - Blob = containerClient.GetBlockBlobClient(typeof(TStoredModel).Name.ToLower()); - if (!await Blob.ExistsAsync()) - { - using (var ms = new MemoryStream()) - { - ms.Write(Encoding.UTF8.GetBytes("[]")); - ms.Seek(0, SeekOrigin.Begin); - await Blob.UploadAsync(ms); - } - CachedCollection = new List(); - } - _isInitialized = true; - } - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AzureBlobStorageDataStoreConfiguration.cs b/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AzureBlobStorageDataStoreConfiguration.cs deleted file mode 100644 index ecf0b0d..0000000 --- a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/AzureBlobStorageDataStoreConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System.ComponentModel; - -namespace AuthJanitor.Integrations.DataStores.AzureBlobStorage -{ - public class AzureBlobStorageDataStoreConfiguration - { - [Description("Connection string for Azure Blob Storage")] - public string ConnectionString { get; set; } - - [Description("Blob storage container name")] - public string Container { get; set; } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/Extensions.cs b/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/Extensions.cs deleted file mode 100644 index de30449..0000000 --- a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/Extensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using AuthJanitor.DataStores; -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace AuthJanitor.Integrations.DataStores.AzureBlobStorage -{ - public static class Extensions - { - public static void AddAJAzureBlobStorage(this IServiceCollection serviceCollection, Action configureOptions) - where TStoredModel : IAuthJanitorModel - { - serviceCollection.Configure(configureOptions); - serviceCollection.AddAJAzureBlobStorage(); - } - - public static void AddAJAzureBlobStorage(this IServiceCollection serviceCollection, Action configureOptions) - { - serviceCollection.Configure(configureOptions); - serviceCollection.AddSingleton, AzureBlobStorageDataStore>(); - serviceCollection.AddSingleton, AzureBlobStorageDataStore>(); - serviceCollection.AddSingleton, AzureBlobStorageDataStore>(); - serviceCollection.AddSingleton, AzureBlobStorageDataStore>(); - } - - public static void AddAJAzureBlobStorage(this IServiceCollection serviceCollection) - where TStoredModel : IAuthJanitorModel - { - serviceCollection.AddSingleton, AzureBlobStorageDataStore>(); - } - } -} \ No newline at end of file diff --git a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/TimeSpanConverter.cs b/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/TimeSpanConverter.cs deleted file mode 100644 index 4e04bd3..0000000 --- a/src/AuthJanitor.Integrations.DataStores.AzureBlobStorage/TimeSpanConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace AuthJanitor.Integrations.DataStores.AzureBlobStorage -{ - internal sealed class TimeSpanConverter : JsonConverter - { - public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return TimeSpan.Parse(reader.GetString()); - } - - public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString()); - } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/AuthJanitor.Integrations.DataStores.EntityFrameworkCore.csproj b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/AuthJanitor.Integrations.DataStores.EntityFrameworkCore.csproj deleted file mode 100644 index e7c459c..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/AuthJanitor.Integrations.DataStores.EntityFrameworkCore.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netstandard2.1 - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/AuthJanitorDbContext.cs b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/AuthJanitorDbContext.cs deleted file mode 100644 index 3f1402e..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/AuthJanitorDbContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using Microsoft.EntityFrameworkCore; - -namespace AuthJanitor.Integrations.DataStores.EntityFrameworkCore -{ - public class AuthJanitorDbContext : DbContext - { - public AuthJanitorDbContext() - { - } - - public AuthJanitorDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions) - { - } - - public DbSet ManagedSecrets { get; set; } - public DbSet RekeyingTasks { get; set; } - public DbSet Resources { get; set; } - public DbSet ScheduleWindows { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - base.OnConfiguring(optionsBuilder); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly); - } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ManagedSecretConfiguration.cs b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ManagedSecretConfiguration.cs deleted file mode 100644 index 6f4ef74..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ManagedSecretConfiguration.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace AuthJanitor.Integrations.DataStores.EntityFrameworkCore.Configurations -{ - public class ManagedSecretConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(x => x.ObjectId); - // TODO which properties are required? - builder.Property(x => x.Name).IsRequired(); - builder.Property(x => x.AdminEmails) - .HasConversion( - v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject>(v)); - builder.Property(x => x.ResourceIds) - .HasConversion( - v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject>(v)); - // TODO: We have properties to ignore - are they ignored since they don't have a setter? - } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/RekeyingTaskConfiguration.cs b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/RekeyingTaskConfiguration.cs deleted file mode 100644 index a4cccbe..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/RekeyingTaskConfiguration.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Providers; -using AuthJanitor.UI.Shared.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace AuthJanitor.Integrations.DataStores.EntityFrameworkCore.Configurations -{ - public class RekeyingTaskConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(x => x.ObjectId); - // TODO which properties are required? - builder.Property(x => x.Attempts) - .HasConversion( - v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject>(v)); - } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ResourceConfiguration.cs b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ResourceConfiguration.cs deleted file mode 100644 index 14f7bb9..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ResourceConfiguration.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace AuthJanitor.Integrations.DataStores.EntityFrameworkCore.Configurations -{ - public class ResourceConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(x => x.ObjectId); - // TODO which properties are required? - builder.Property(x => x.Name).IsRequired(); - builder.Property(x => x.ProviderType).IsRequired(); - } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ScheduleWindowConfiguration.cs b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ScheduleWindowConfiguration.cs deleted file mode 100644 index 5cf16e9..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/Configurations/ScheduleWindowConfiguration.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Newtonsoft.Json; -using System.Collections.Generic; - -namespace AuthJanitor.Integrations.DataStores.EntityFrameworkCore.Configurations -{ - public class ScheduleWindowConfiguration : IEntityTypeConfiguration - { - public void Configure(EntityTypeBuilder builder) - { - builder.HasKey(x => x.ObjectId); - builder.Property(x => x.CronStrings).IsRequired(); - builder.Property(x => x.CronStrings) - .HasConversion( - v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject>(v)); - } - } -} diff --git a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/EntityFrameworkCoreDataStore.cs b/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/EntityFrameworkCoreDataStore.cs deleted file mode 100644 index c763f4a..0000000 --- a/src/AuthJanitor.Integrations.DataStores.EntityFrameworkCore/EntityFrameworkCoreDataStore.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AuthJanitor.Integrations.DataStores.EntityFrameworkCore -{ - public class EntityFrameworkCoreDataStore : IDataStore where TStoredModel : class, IAuthJanitorModel - { - private readonly AuthJanitorDbContext dbContext; - - private DbSet Set - { - get - { - return dbContext.Set(); - } - } - - public EntityFrameworkCoreDataStore(AuthJanitorDbContext dbContext) - { - this.dbContext = dbContext; - } - - public async Task ContainsId(Guid objectId, CancellationToken cancellationToken) - { - return (await Set.FirstOrDefaultAsync(x => x.ObjectId == objectId, cancellationToken) != default(TStoredModel)); - } - - public async Task Create(TStoredModel model, CancellationToken cancellationToken) - { - Set.Add(model); - await dbContext.SaveChangesAsync(cancellationToken); - return await Set.FirstOrDefaultAsync(x => x.ObjectId == model.ObjectId, cancellationToken); - } - - public async Task Delete(Guid objectId, CancellationToken cancellationToken) - { - var entity = await Set.FirstOrDefaultAsync(x => x.ObjectId == objectId, cancellationToken); - if (entity != null) - { - Set.Remove(entity); - await dbContext.SaveChangesAsync(cancellationToken); - } - } - - public async Task> Get(CancellationToken cancellationToken) - { - return await Set.ToListAsync(cancellationToken); - } - - public async Task> Get(Func predicate, CancellationToken cancellationToken) - { - return await Task.FromResult(Set.Where(predicate).ToList()); - } - - public Task GetOne(Guid objectId, CancellationToken cancellationToken) - { - return Set.FirstOrDefaultAsync(x => x.ObjectId == objectId, cancellationToken); - } - - public Task GetOne(Func predicate, CancellationToken cancellationToken) - { - return Task.FromResult(Set.Where(predicate).FirstOrDefault()); - } - - public async Task Update(TStoredModel model, CancellationToken cancellationToken) - { - var entity = await Set.FirstOrDefaultAsync(x => x.ObjectId == model.ObjectId, cancellationToken); - if (entity != null) - { - Set.Remove(entity); - Set.Add(model); - await dbContext.SaveChangesAsync(cancellationToken); - } - return await Set.FirstOrDefaultAsync(x => x.ObjectId == model.ObjectId, cancellationToken); - } - } -} diff --git a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityService.cs b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityService.cs deleted file mode 100644 index 16b66a4..0000000 --- a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityService.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.IdentityServices; -using Azure.Core; -using Azure.Identity; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Security.Claims; -using System.Text.Json; -using System.Threading.Tasks; - -namespace AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory -{ - /// - /// Implements an identity service which reads from the HttpContext to integrate with Azure Active Directory - /// - public class AzureADIdentityService : IIdentityService - { - private const string HTTP_HEADER_NAME = "AuthJanitor"; - private const string HTTP_HEADER_VALUE = "administrator"; - - public const string AGENT_CREDENTIAL_USERNAME = "application.identity@local"; - public const string DEFAULT_OBO_RESOURCE = "https://management.core.windows.net/"; - - private const string ROLES_CLAIM = "roles"; - private const string ID_TOKEN_OBO_HEADER_NAME = "X-MS-TOKEN-AAD-ID-TOKEN"; - - private AzureADIdentityServiceConfiguration Configuration { get; } - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly JsonSerializerOptions _serializerOptions = new JsonSerializerOptions() - { - // Upstream bug: https://github.com/dotnet/runtime/issues/30255 - Converters = { new StringToIntJsonConverter(false), new StringToLongJsonConverter(false) } - }; - - private readonly ILogger _logger; - - /// - /// Implements an identity service which reads from the HttpContext to integrate with Azure Active Directory - /// - public AzureADIdentityService( - IOptions configuration, - IHttpContextAccessor httpContextAccessor, - ILogger logger) - { - Configuration = configuration.Value; - _httpContextAccessor = httpContextAccessor; - _logger = logger; - } - -#if DEBUG - private static bool IsRunningLocally => string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID")); -#endif - - /// - /// Return if there is currently a user logged in (with any valid AuthJanitor role) - /// - public bool IsUserLoggedIn => -#if DEBUG - IsRunningLocally ? true : -#endif - (_httpContextAccessor.HttpContext?.Request?.Headers[HTTP_HEADER_NAME] ?? string.Empty) == HTTP_HEADER_VALUE && - _httpContextAccessor.HttpContext?.User.Claims != null && - GetClaimsInternal(ROLES_CLAIM).Any(r => AuthJanitorRoles.ALL_ROLES.Contains(r)); - - /// - /// Return the current user's name - /// - public string UserName => - IsUserLoggedIn ? $"{GetClaimInternal(ClaimTypes.GivenName)} {GetClaimInternal(ClaimTypes.Surname)}" - : string.Empty; - - /// - /// Return the current user's e-mail address - /// - public string UserEmail => - IsUserLoggedIn ? GetClaimInternal(ClaimTypes.Email) - : string.Empty; - - /// - /// Return a list of the current user's roles - /// - public string[] UserRoles => -#if DEBUG - IsRunningLocally ? new string[] { AuthJanitorRoles.GlobalAdmin } : -#endif - IsUserLoggedIn ? GetClaimsInternal(ROLES_CLAIM).ToArray() - : new string[0]; - - /// - /// If the currently logged in user has the given role (or is a GlobalAdmin) - /// - /// Role to test - public bool CurrentUserHasRole(string authJanitorRole) => -#if DEBUG - IsRunningLocally ? true : -#endif - IsUserLoggedIn ? GetClaimsInternal(ROLES_CLAIM).Any(r => r.Equals(authJanitorRole) || r.Equals(AuthJanitorRoles.GlobalAdmin)) - : false; - - /// - /// Retrieve the Access Token for the current underlying MSI/SP identity - /// - /// Scopes to request with Access Token - /// Access Token - public Task GetAccessTokenForApplicationAsync(params string[] scopes) - { - return new DefaultAzureCredential(new DefaultAzureCredentialOptions() - { - ExcludeEnvironmentCredential = false, - ExcludeInteractiveBrowserCredential = true, - ExcludeManagedIdentityCredential = false, - ExcludeSharedTokenCacheCredential = true - }).GetTokenAsync(new TokenRequestContext(scopes)) - .AsTask() - .ContinueWith(t => new AccessTokenCredential() - { - Username = AGENT_CREDENTIAL_USERNAME, - AccessToken = t.Result.Token, - ExpiresOnDateTime = t.Result.ExpiresOn - }); - } - - /// - /// Retrieve an Access Token for a resource on behalf of the current user - /// - /// Resource to perform on-behalf-of exchange for - /// Access Token - public async Task GetAccessTokenOnBehalfOfCurrentUserAsync(string resource = DEFAULT_OBO_RESOURCE) - { - if (!_httpContextAccessor.HttpContext.Request.Headers.ContainsKey(ID_TOKEN_OBO_HEADER_NAME)) - throw new InvalidOperationException($"HttpContext Request Headers does not contain '{ID_TOKEN_OBO_HEADER_NAME}'"); - - var idToken = _httpContextAccessor.HttpContext.Request.Headers[ID_TOKEN_OBO_HEADER_NAME].FirstOrDefault(); - if (string.IsNullOrEmpty(idToken)) - throw new InvalidOperationException($"HttpContext Request Header '{ID_TOKEN_OBO_HEADER_NAME}' does not contain a value"); - - var dict = new Dictionary() - { - { "grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer" }, - { "client_id", Configuration.ClientId }, - { "client_secret", Configuration.ClientSecret }, - { "assertion", idToken }, - //{ "scope", resource }, - { "resource", resource }, - { "requested_token_use", "on_behalf_of" } - }; - - var result = await new HttpClient().PostAsync("https://login.microsoftonline.com/" + - Configuration.TenantId + - //"/oauth2/v2.0/token", - "/oauth2/token", - new FormUrlEncodedContent(dict)); - - var resultContent = await result.Content.ReadAsStringAsync(); - var token = JsonSerializer.Deserialize(resultContent, _serializerOptions); - - if (!result.IsSuccessStatusCode || token.IsInError) - { - _logger.LogError("Error exchanging token: {errorType} ({subErrorType}) -- Error code(s) {errorCodes}", token.ErrorType, token.SubErrorType, token.ErrorCodes); - } - - return token; - } - - private IEnumerable GetClaimsInternal(string claimType) => -#if DEBUG - IsRunningLocally ? new string[] { "#DEBUG#" } : -#endif - (_httpContextAccessor.HttpContext == null || - _httpContextAccessor.HttpContext.User == null || - _httpContextAccessor.HttpContext.User.Claims == null) ? new string[0] : - - _httpContextAccessor.HttpContext.User.Claims - .Where(c => c.Type == claimType) - .Select(c => c.Value) - .Distinct() - .ToArray(); - - private string GetClaimInternal(string claimType) => GetClaimsInternal(claimType).FirstOrDefault(); - } -} diff --git a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceConfiguration.cs b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceConfiguration.cs index 9b5caef..4c2879a 100644 --- a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceConfiguration.cs +++ b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceConfiguration.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using AuthJanitor.Providers; using System.ComponentModel; namespace AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory @@ -14,5 +15,8 @@ public class AzureADIdentityServiceConfiguration [Description("Azure AD tenant ID")] public string TenantId { get; set; } + + [Description("Token Source")] + public TokenSources TokenSource { get; set; } } } diff --git a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceManager.cs b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceManager.cs index 3be1cca..45efa5a 100644 --- a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceManager.cs +++ b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/AzureADIdentityServiceManager.cs @@ -19,20 +19,20 @@ public class AzureADIdentityServiceManager : IIdentityServiceManager private const string PERMISSION_APPROLE_ASSIGNMENT_READWRITE_ALL = "appRoleAssignment.readWrite.all"; private AzureADIdentityServiceConfiguration Configuration { get; } - private IIdentityService _identityService; private readonly ILogger _logger; + private readonly ITokenCredentialProvider _tokenCredentialProvider; /// /// Implements user management and role controls for an identity service which reads from the HttpContext to integrate with Azure Active Directory /// public AzureADIdentityServiceManager( + ITokenCredentialProvider tokenCredentialProvider, IOptions configuration, - IIdentityService identityService, ILogger logger) { + _tokenCredentialProvider = tokenCredentialProvider; Configuration = configuration.Value; - _identityService = identityService; _logger = logger; } @@ -164,7 +164,7 @@ private async Task GetAppServicePrincipalAsync(GraphServiceCli private async Task GetGraphServiceClientAsync(params string[] scopes) { - var token = await _identityService.GetAccessTokenOnBehalfOfCurrentUserAsync("https://graph.microsoft.com/");// + string.Join('+', scopes)); + var token = await _tokenCredentialProvider.GetToken(Configuration.TokenSource, string.Empty); if (!token.IsInError) _logger.LogInformation("Got token for user {user} -- resource {resource} -- scope {scope}", token.Username, token.Resource, token.Scope); diff --git a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/Extensions.cs b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/Extensions.cs index d9a2f50..50d74f2 100644 --- a/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/Extensions.cs +++ b/src/AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory/Extensions.cs @@ -8,12 +8,6 @@ namespace AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory { public static class Extensions { - public static void AddAJAzureActiveDirectory(this IServiceCollection serviceCollection, Action configureOptions) - { - serviceCollection.Configure(configureOptions); - serviceCollection.AddSingleton(); - } - public static void AddAJAzureActiveDirectoryManager(this IServiceCollection serviceCollection, Action configureOptions) { serviceCollection.Configure(configureOptions); diff --git a/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProvider.cs b/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProvider.cs index 5366b4a..cc4a4a5 100644 --- a/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProvider.cs +++ b/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProvider.cs @@ -15,16 +15,16 @@ namespace AuthJanitor.Integrations.SecureStorage.AzureKeyVault public class KeyVaultSecureStorageProvider : ISecureStorage { private KeyVaultSecureStorageProviderConfiguration Configuration { get; } - private readonly IIdentityService _identityService; private readonly ICryptographicImplementation _cryptographicImplementation; + private readonly ITokenCredentialProvider _tokenCredentialProvider; public KeyVaultSecureStorageProvider( IOptions configuration, - IIdentityService identityService, + ITokenCredentialProvider tokenCredentialProvider, ICryptographicImplementation cryptographicImplementation) { Configuration = configuration.Value; - _identityService = identityService; + _tokenCredentialProvider = tokenCredentialProvider; _cryptographicImplementation = cryptographicImplementation; if (_cryptographicImplementation == null) @@ -63,7 +63,7 @@ public async Task Retrieve(Guid persistenceId) } private Task GetClient() => - _identityService.GetAccessTokenForApplicationAsync() + _tokenCredentialProvider.GetToken(Configuration.TokenSource, string.Empty) .ContinueWith(t => new SecretClient( new Uri($"https://{Configuration.VaultName}.vault.azure.net/"), ExistingTokenCredential.FromAccessToken(t.Result))); diff --git a/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProviderConfiguration.cs b/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProviderConfiguration.cs index d035921..5bbf440 100644 --- a/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProviderConfiguration.cs +++ b/src/AuthJanitor.Integrations.SecureStorage.AzureKeyVault/KeyVaultSecureStorageProviderConfiguration.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using AuthJanitor.Providers; using System.ComponentModel; namespace AuthJanitor.Integrations.SecureStorage.AzureKeyVault @@ -11,5 +12,7 @@ public class KeyVaultSecureStorageProviderConfiguration [Description("Object prefix")] public string Prefix { get; set; } = "AJPersist-"; + + public TokenSources TokenSource { get; set; } } } diff --git a/src/AuthJanitor.Repository/AuthJanitor.Repository.csproj b/src/AuthJanitor.Repository/AuthJanitor.Repository.csproj new file mode 100644 index 0000000..96c090d --- /dev/null +++ b/src/AuthJanitor.Repository/AuthJanitor.Repository.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + diff --git a/src/AuthJanitor.Repository/AuthJanitorDbContext.cs b/src/AuthJanitor.Repository/AuthJanitorDbContext.cs new file mode 100644 index 0000000..5837aa3 --- /dev/null +++ b/src/AuthJanitor.Repository/AuthJanitorDbContext.cs @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using AuthJanitor.Repository.Models; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Newtonsoft.Json; +using System; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; + +namespace AuthJanitor.Repository +{ + public class AuthJanitorDbContext : DbContext + { + public DbSet Resources { get; set; } + public DbSet DependencyGroups { get; set; } + public DbSet ResourceDependencyGroups { get; set; } + public DbSet DependencyGroupRotations { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlite(CreateInMemoryDatabase()); + } + + private static DbConnection CreateInMemoryDatabase() + { + var connection = new SqliteConnection("Filename=:memory:"); + connection.Open(); + return connection; + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .Property(e => e.Parameters) + .HasConversion(GetJsonConverter()); + + modelBuilder + .Entity() + .Property(e => e.WorkflowActions) + .HasConversion(GetJsonConverter()); + + modelBuilder + .Entity() + .Property(e => e.DefaultParameters) + .HasConversion(GetJsonConverter()); + + // *:* join table + modelBuilder.Entity() + .HasKey(rdg => new { rdg.DependencyGroupId, rdg.ResourceId }); + modelBuilder.Entity() + .HasOne(rdg => rdg.Resource) + .WithMany(r => r.DependencyGroups) + .HasForeignKey(rdg => rdg.ResourceId); + modelBuilder.Entity() + .HasOne(rdg => rdg.DependencyGroup) + .WithMany(r => r.Resources) + .HasForeignKey(rdg => rdg.DependencyGroupId); + + // 1:* + modelBuilder.Entity() + .HasOne(dgr => dgr.DependencyGroup) + .WithMany(dg => dg.Rotations) + .HasForeignKey(dgr => dgr.DependencyGroupId); + + modelBuilder.Entity() + .HasIndex(dgr => dgr.DependencyGroupId); + modelBuilder.Entity() + .HasIndex(dgr => new { dgr.HasExecuted, dgr.ExecutionTime }); + } + + public async Task RunScheduledTasks( + AuthJanitorService svc, + Func> getOboCallback, + Func> getMsiCallback) + { + // schedule new tasks based on things that have passed + var targets = await DependencyGroups + .Where(dg => dg.LastRotation.Add(dg.ValidPeriod) < DateTimeOffset.UtcNow) + .Where(dg => !dg.Rotations.All(r => r.HasExecuted)) + .ToListAsync(); + + await Task.WhenAll(targets.Select(item => DependencyGroupRotations.CreateNewTask( + item, + item.LastRotation.Add(item.ValidPeriod)))); + await SaveChangesAsync(); + + // Execute/Dispatch tasks on schedule + var tasks = await DependencyGroupRotations + .Where(dgr => !dgr.HasExecuted) + .Where(dgr => dgr.ExecutionTime < DateTimeOffset.UtcNow) + .ToListAsync(); + + foreach (var task in tasks) + { + var result = await svc.ExecuteAsync( + task.DependencyGroup.ValidPeriod, + async pwac => + { + // invoked periodically and at completion + task.WorkflowActions = pwac; + if (pwac.HasBeenExecutedSuccessfully) + task.DependencyGroup.LastRotation = DateTimeOffset.UtcNow; + await SaveChangesAsync(); + }, + task.DependencyGroup.Resources.Select(r => + r.Resource.Parameters).ToArray()); + } + } + + private ValueConverter GetJsonConverter() => + new ValueConverter( + v => JsonConvert.SerializeObject(v), + v => JsonConvert.DeserializeObject(v)); + } +} diff --git a/src/AuthJanitor.Repository/Extensions.cs b/src/AuthJanitor.Repository/Extensions.cs new file mode 100644 index 0000000..e7d7062 --- /dev/null +++ b/src/AuthJanitor.Repository/Extensions.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using AuthJanitor.Repository.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace AuthJanitor.Repository +{ + public static class Extensions + { + public static void TryInitializeDatabase(this IServiceProvider app) + { + var serviceScopeFactory = app.GetRequiredService(); + using (var serviceScope = serviceScopeFactory.CreateScope()) + { + var dbContext = serviceScope.ServiceProvider.GetService(); + dbContext.Database.EnsureCreated(); + } + } + + public static async Task CreateFromSuggestion( + this DbSet resources, + ProviderResourceSuggestion suggestion) => + (await resources.AddAsync(new ResourceModel() + { + DescriptiveName = suggestion.Name, + Parameters = suggestion.Parameters, + })).Entity; + + public static async Task CreateFromParameters( + this DbSet resources, + ProviderExecutionParameters executionParameters, + string name = "New Resource") => + (await resources.AddAsync(new ResourceModel() + { + DescriptiveName = name, + Parameters = executionParameters, + })).Entity; + + public static async Task Create( + this DbSet resourcesDependencyGroups, + DependencyGroupModel resourceGroup, + ResourceModel resource) => + (await resourcesDependencyGroups.AddAsync(new ResourceDependencyGroupModel() + { + Resource = resource, + DependencyGroup = resourceGroup + })).Entity; + + private static AuthJanitorDbContext GetContext( + this DbSet dbSet) where T : class => + dbSet.GetService().Context as AuthJanitorDbContext; + + public static async Task CreateFromSuggestion( + this DbSet dependencyGroups, + ProviderResourceSuggestion suggestion, + string name) + { + var resources = + (await Task.WhenAll(suggestion.ResourcesAddressingThis.Select( + async r => await dependencyGroups.GetContext() + .Resources.CreateFromSuggestion(r)))) + .ToList(); + resources.Add(await dependencyGroups.GetContext() + .Resources.CreateFromSuggestion(suggestion)); + + var resourceGroup = new DependencyGroupModel() + { + Name = name + }; + await dependencyGroups.AddAsync(resourceGroup); + + var membership = await Task.WhenAll(resources.Select(async r => + await dependencyGroups.GetContext().ResourceDependencyGroups.Create( + resourceGroup, r))); + resourceGroup.Resources = membership; + + return resourceGroup; + } + + public static async Task CreateNewTask( + this DbSet rotations, + DependencyGroupModel dependencyGroup, + DateTimeOffset executionTime) => + (await rotations.AddAsync(new DependencyGroupRotationModel() + { + DependencyGroup = dependencyGroup, + ExecutionTime = executionTime, + HasExecuted = false, + + })).Entity; + } +} diff --git a/src/AuthJanitor.Repository/Models/DependencyGroupModel.cs b/src/AuthJanitor.Repository/Models/DependencyGroupModel.cs new file mode 100644 index 0000000..53d19c9 --- /dev/null +++ b/src/AuthJanitor.Repository/Models/DependencyGroupModel.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AuthJanitor.Repository.Models +{ + [Table("DependencyGroups")] + public class DependencyGroupModel + { + [Key] + public Guid DependencyGroupId { get; set; } = Guid.NewGuid(); + + public string Name { get; set; } + public TimeSpan ValidPeriod { get; set; } + public DateTimeOffset LastRotation { get; set; } = DateTimeOffset.MinValue; + + public ProviderExecutionParameters DefaultParameters { get; set; } = + new ProviderExecutionParameters(); + + public ICollection Resources { get; set; } + = new List(); + + public ICollection Rotations { get; set; } + = new List(); + } +} diff --git a/src/AuthJanitor.Repository/Models/DependencyGroupRotationModel.cs b/src/AuthJanitor.Repository/Models/DependencyGroupRotationModel.cs new file mode 100644 index 0000000..d895b4d --- /dev/null +++ b/src/AuthJanitor.Repository/Models/DependencyGroupRotationModel.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AuthJanitor.Repository.Models +{ + [Table("DependencyGroupRotations")] + public class DependencyGroupRotationModel + { + [Key] + public Guid ResourceGroupRotationId { get; set; } = Guid.NewGuid(); + + public DateTimeOffset ExecutionTime { get; set; } + public bool HasExecuted { get; set; } + + public Guid DependencyGroupId { get; set; } + public DependencyGroupModel DependencyGroup { get; set; } + + public DateTimeOffset LastRotated => WorkflowActions.FinishedExecution; + + [System.ComponentModel.DataAnnotations.Schema.NotMapped] // TODO!!!! + public ProviderWorkflowActionCollection WorkflowActions { get; set; } = + new ProviderWorkflowActionCollection(); + } +} diff --git a/src/AuthJanitor.Repository/Models/ResourceDependencyGroupModel.cs b/src/AuthJanitor.Repository/Models/ResourceDependencyGroupModel.cs new file mode 100644 index 0000000..9a5bedd --- /dev/null +++ b/src/AuthJanitor.Repository/Models/ResourceDependencyGroupModel.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AuthJanitor.Repository.Models +{ + [Table("ResourceDependencyGroups")] + public class ResourceDependencyGroupModel + { + public Guid ResourceId { get; set; } + public Guid DependencyGroupId { get; set; } + + public ResourceModel Resource { get; set; } + public DependencyGroupModel DependencyGroup { get; set; } + } +} diff --git a/src/AuthJanitor.Repository/Models/ResourceModel.cs b/src/AuthJanitor.Repository/Models/ResourceModel.cs new file mode 100644 index 0000000..2f3a783 --- /dev/null +++ b/src/AuthJanitor.Repository/Models/ResourceModel.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +using AuthJanitor.Providers; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace AuthJanitor.Repository.Models +{ + [Table("Resources")] + public class ResourceModel + { + [Key] + public Guid ResourceId { get; set; } = Guid.NewGuid(); + + public string DescriptiveName { get; set; } + public ProviderExecutionParameters Parameters { get; set; } = + new ProviderExecutionParameters(); + + public ICollection DependencyGroups { get; set; } + = new List(); + } +} diff --git a/src/AuthJanitor.Tests/AuthJanitor.Tests.csproj b/src/AuthJanitor.Tests/AuthJanitor.Tests.csproj index baddf34..dd03eca 100644 --- a/src/AuthJanitor.Tests/AuthJanitor.Tests.csproj +++ b/src/AuthJanitor.Tests/AuthJanitor.Tests.csproj @@ -21,9 +21,6 @@ - - - diff --git a/src/AuthJanitor.Tests/CryptographicTests.cs b/src/AuthJanitor.Tests/CryptographicTests.cs index 09693bb..2e8f7d3 100644 --- a/src/AuthJanitor.Tests/CryptographicTests.cs +++ b/src/AuthJanitor.Tests/CryptographicTests.cs @@ -1,8 +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; using System; using System.Security.Cryptography; diff --git a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ManagedSecretTests.cs b/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ManagedSecretTests.cs deleted file mode 100644 index f793146..0000000 --- a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ManagedSecretTests.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared; -using AuthJanitor.UI.Shared.Models; -using System; - -namespace AuthJanitor.Tests.EntityFrameworkCoreDataStore -{ - public class EF_ManagedSecretTests : EF_TestsBase - { - protected override ManagedSecret CreateModel() - { - return new ManagedSecret() - { - ObjectId = Guid.NewGuid(), - AdminEmails = new string[] { "admin@admin.com" }, - Description = "Description", - Name = "Name", - LastChanged = new DateTimeOffset(DateTime.Now), - Nonce = "Nonce", - TaskConfirmationStrategies = TaskConfirmationStrategies.AdminCachesSignOff, - ValidPeriod = new TimeSpan(24, 0, 0), - ResourceIds = new Guid[] { Guid.NewGuid() } - }; - } - - protected override ManagedSecret UpdatedModel() - { - return new ManagedSecret() - { - ObjectId = model.ObjectId, - AdminEmails = new string[] { "admin@admin.com" }, - Description = "Another Description", - Name = "Another Name", - LastChanged = new DateTimeOffset(DateTime.Now), - Nonce = "Another Nonce", - TaskConfirmationStrategies = TaskConfirmationStrategies.AdminCachesSignOff, - ValidPeriod = new TimeSpan(24, 0, 0), - ResourceIds = new Guid[] { Guid.NewGuid() } - }; - } - - protected override bool CompareModel(ManagedSecret model1, ManagedSecret model2) - { - if (model1.ObjectId != model2.ObjectId || - model1.Description != model2.Description || - model1.Name != model2.Name || - model1.LastChanged != model2.LastChanged || - model1.Nonce != model2.Nonce || - model1.TaskConfirmationStrategies != model2.TaskConfirmationStrategies || - model1.ValidPeriod != model2.ValidPeriod) - return false; - if (!model1.AdminEmails.Equals(model2.AdminEmails)) - return false; - if (!model1.ResourceIds.Equals(model2.ResourceIds)) - return false; - return true; - } - } -} diff --git a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_RekeyingTaskTests.cs b/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_RekeyingTaskTests.cs deleted file mode 100644 index b655399..0000000 --- a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_RekeyingTaskTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.Providers; -using AuthJanitor.UI.Shared; -using AuthJanitor.UI.Shared.Models; -using System; -using System.Collections.Generic; - -namespace AuthJanitor.Tests.EntityFrameworkCoreDataStore -{ - public class EF_RekeyingTaskTests : EF_TestsBase - { - protected override RekeyingTask CreateModel() - { - return new RekeyingTask() - { - ObjectId = Guid.NewGuid(), - AvailabilityScheduleId = Guid.NewGuid(), - Attempts = new List(), - // todo: attempt model? - ConfirmationType = TaskConfirmationStrategies.AdminCachesSignOff, - Expiry = new DateTimeOffset(DateTime.Now), - ManagedSecretId = Guid.NewGuid(), - PersistedCredentialId = Guid.NewGuid(), - PersistedCredentialUser = "CredentialUser", - RekeyingInProgress = false, - Queued = new DateTimeOffset(DateTime.Now), - RekeyingCompleted = false, - RekeyingFailed = true - }; - } - - protected override RekeyingTask UpdatedModel() - { - return new RekeyingTask() - { - ObjectId = model.ObjectId, - AvailabilityScheduleId = Guid.NewGuid(), - Attempts = new List(), - // todo: attempt model? - ConfirmationType = TaskConfirmationStrategies.AdminCachesSignOff, - Expiry = new DateTimeOffset(DateTime.Now), - ManagedSecretId = Guid.NewGuid(), - PersistedCredentialId = Guid.NewGuid(), - PersistedCredentialUser = "Another CredentialUser", - RekeyingInProgress = false, - Queued = new DateTimeOffset(DateTime.Now), - RekeyingCompleted = false, - RekeyingFailed = true - }; - } - - protected override bool CompareModel(RekeyingTask model1, RekeyingTask model2) - { - if (model1.ObjectId != model2.ObjectId || - model1.AvailabilityScheduleId != model2.AvailabilityScheduleId || - model1.Attempts != model2.Attempts || - model1.ConfirmationType != model2.ConfirmationType || - model1.Expiry != model2.Expiry || - model1.ManagedSecretId != model2.ManagedSecretId || - model1.PersistedCredentialId != model2.PersistedCredentialId || - model1.PersistedCredentialUser != model2.PersistedCredentialUser || - model1.RekeyingInProgress != model2.RekeyingInProgress || - model1.Queued != model2.Queued || - model1.RekeyingCompleted != model2.RekeyingCompleted || - model1.RekeyingFailed != model2.RekeyingFailed) - return false; - if (!model1.Attempts.Equals(model2.Attempts)) - return false; - return true; - } - } -} diff --git a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ResourceTests.cs b/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ResourceTests.cs deleted file mode 100644 index 81c9bc5..0000000 --- a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ResourceTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using System; - -namespace AuthJanitor.Tests.EntityFrameworkCoreDataStore -{ - public class EF_ResourceTests : EF_TestsBase - { - protected override Resource CreateModel() - { - return new Resource() - { - ObjectId = Guid.NewGuid(), - Description = "Description", - Name = "Name", - ProviderConfiguration = "ProviderConfiguration", - ProviderType = "ProviderType" - }; - } - - protected override Resource UpdatedModel() - { - return new Resource() - { - ObjectId = model.ObjectId, - Description = "Another Description", - Name = "Another Name", - ProviderConfiguration = "Another ProviderConfiguration", - ProviderType = "Another ProviderType" - }; - } - - protected override bool CompareModel(Resource model1, Resource model2) - { - if (model1.ObjectId != model2.ObjectId || - model1.Description != model2.Description || - model1.Name != model2.Name || - model1.ProviderConfiguration != model2.ProviderConfiguration || - model1.ProviderType != model2.ProviderType) - return false; - return true; - } - } -} diff --git a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ScheduleWindowTests.cs b/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ScheduleWindowTests.cs deleted file mode 100644 index ca0fed5..0000000 --- a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_ScheduleWindowTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.UI.Shared.Models; -using System; -using System.Linq; - -namespace AuthJanitor.Tests.EntityFrameworkCoreDataStore -{ - public class EF_ScheduleWindowTests : EF_TestsBase - { - protected override ScheduleWindow CreateModel() - { - return new ScheduleWindow() - { - ObjectId = Guid.NewGuid(), - CronStrings = new string[] { "TODO" } - }; - } - - protected override ScheduleWindow UpdatedModel() - { - return new ScheduleWindow() - { - ObjectId = model.ObjectId, - CronStrings = new string[] { "TODO2" } - }; - } - - protected override bool CompareModel(ScheduleWindow model1, ScheduleWindow model2) - { - if (model1.ObjectId != model2.ObjectId) - return false; - if (model1.CronStrings.Count() != model2.CronStrings.Count()) - return false; - if (!model1.CronStrings.Equals(model2.CronStrings)) - return false; - return true; - } - } -} diff --git a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_TestsBase.cs b/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_TestsBase.cs deleted file mode 100644 index 9d5187e..0000000 --- a/src/AuthJanitor.Tests/EntityFrameworkCoreDataStore/EF_TestsBase.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -using AuthJanitor.DataStores; -using AuthJanitor.Integrations.DataStores.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace AuthJanitor.Tests.EntityFrameworkCoreDataStore -{ - public abstract class EF_TestsBase where TStoredModel : class, IAuthJanitorModel - { - private AuthJanitorDbContext dbContext = null; - protected TStoredModel model = null; - - protected EntityFrameworkCoreDataStore GetDataStore() - { - if (model == null) - model = CreateModel(); - if (dbContext == null) - dbContext = new AuthJanitorDbContext(new DbContextOptionsBuilder().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options); - return new EntityFrameworkCoreDataStore(dbContext); - } - - protected Task CleanModel() - { - dbContext.Set().RemoveRange(dbContext.Set().ToArray()); - return dbContext.SaveChangesAsync(); - } - - protected abstract TStoredModel CreateModel(); - protected abstract TStoredModel UpdatedModel(); - protected abstract bool CompareModel(TStoredModel model1, TStoredModel model2); - - [Fact] - public async Task ValidatesCreate() - { - var datastore = GetDataStore(); - var storedModel = await datastore.Create(model, CancellationToken.None); - Assert.True(CompareModel(model, storedModel)); - var fetchedModel = await datastore.GetOne(model.ObjectId, CancellationToken.None); - Assert.True(CompareModel(model, fetchedModel)); - await CleanModel(); - } - - [Fact] - public async Task ValidatesUpdate() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var updatedModel = UpdatedModel(); - var storedModel = await datastore.Update(updatedModel, CancellationToken.None); - Assert.True(CompareModel(updatedModel, storedModel)); - var fetchedModel = await datastore.GetOne(model.ObjectId, CancellationToken.None); - Assert.True(CompareModel(updatedModel, fetchedModel)); - await CleanModel(); - } - - [Fact] - public async Task ValidatesGet() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var models = await datastore.Get(CancellationToken.None); - Assert.Equal(1, models.Count); - Assert.True(CompareModel(model, models.First())); - await CleanModel(); - } - - [Fact] - public async Task ValidatesGetWithPredicateReturnsModelResult() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var models = await datastore.Get(x => x.ObjectId == model.ObjectId, CancellationToken.None); - Assert.Equal(1, models.Count); - Assert.True(CompareModel(model, models.First())); - await CleanModel(); - } - - [Fact] - public async Task ValidatesGetWithPredicateReturnsNoResult() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var models = await datastore.Get(x => x.ObjectId == Guid.NewGuid(), CancellationToken.None); - Assert.Equal(0, models.Count); - await CleanModel(); - } - - [Fact] - public async Task ValidatesGetOne() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var modelOne = await datastore.GetOne(model.ObjectId, CancellationToken.None); - Assert.True(CompareModel(model, modelOne)); - await CleanModel(); - } - - [Fact] - public async Task ValidatesGetOneWithPredicateReturnsModelResult() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var modelOne = await datastore.GetOne(x => x.ObjectId == model.ObjectId, CancellationToken.None); - Assert.True(CompareModel(model, modelOne)); - await CleanModel(); - } - - [Fact] - public async Task ValidatesGetOneWithPredicateReturnsNoResult() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var modelOne = await datastore.GetOne(x => x.ObjectId == Guid.NewGuid(), CancellationToken.None); - Assert.Null(modelOne); - await CleanModel(); - } - - [Fact] - public async Task ValidatesContainsId() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - var exists = await datastore.ContainsId(model.ObjectId, CancellationToken.None); - Assert.True(exists); - await CleanModel(); - } - - [Fact] - public async Task ValidatesDelete() - { - var datastore = GetDataStore(); - await datastore.Create(model, CancellationToken.None); - await datastore.Delete(model.ObjectId, CancellationToken.None); - var models = await datastore.Get(CancellationToken.None); - Assert.Equal(0, models.Count); - await CleanModel(); - } - } -} diff --git a/src/AuthJanitor.sln b/src/AuthJanitor.sln index 0f91b4e..a39209b 100644 --- a/src/AuthJanitor.sln +++ b/src/AuthJanitor.sln @@ -23,24 +23,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Automation", "Automation", "{45A0E86E-0F1A-434E-B3F9-D9AE88C964E1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Functions.AdminApi", "AuthJanitor.Functions.AdminApi\AuthJanitor.Functions.AdminApi.csproj", "{8DBA0BBC-F92B-456D-8342-0C42E39EB81E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Functions.Agent", "AuthJanitor.Functions.Agent\AuthJanitor.Functions.Agent.csproj", "{F9DCAB83-056E-4F4B-A8CA-9DDBBCF08394}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.AspNet.Core", "AuthJanitor.AspNet\AuthJanitor.AspNet.Core.csproj", "{5BDB5B18-884B-4BB5-BF20-A06114C5C928}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.AspNet.AdminUi", "AuthJanitor.AspNet.AdminUi\AuthJanitor.AspNet.AdminUi.csproj", "{1221DBD5-BD6A-482D-B842-AF53611A48A9}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{4AB5E96D-1A78-44E0-A52B-5F1A7ABE3B40}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Providers.AzureMaps", "AuthJanitor.Providers.AzureMaps\AuthJanitor.Providers.AzureMaps.csproj", "{CC3E1903-9A04-4A14-8EDB-59444BB57E05}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Agent Service", "Agent Service", "{0E2B33F8-6CD0-43EE-AA35-337B6798B509}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integrations", "Integrations", "{07EE807A-D975-4B1D-B486-6633ED87B757}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.CryptographicImplementations.Default", "AuthJanitor.Integrations.CryptographicImplementations.Default\AuthJanitor.Integrations.CryptographicImplementations.Default.csproj", "{31D82FCB-3F22-482C-B7A4-F9D9D4A27EF6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.EventSinks.SendGrid", "AuthJanitor.Integrations.EventSinks.SendGrid\AuthJanitor.Integrations.EventSinks.SendGrid.csproj", "{1E8E5F06-CF36-4CC8-95BD-B7E455D1737C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory", "AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory\AuthJanitor.Integrations.IdentityServices.AzureActiveDirectory.csproj", "{367C9AAB-6A24-4DC8-A69C-A22039CC11C2}" @@ -49,8 +35,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.Se EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Core", "AuthJanitor.Core\AuthJanitor.Core.csproj", "{F103367D-F5FB-43A6-AA52-6B9A0DD66862}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Integrations.DataStores.AzureBlobStorage", "AuthJanitor.Integrations.DataStores.AzureBlobStorage\AuthJanitor.Integrations.DataStores.AzureBlobStorage.csproj", "{2ED2CBEF-1B5F-4D0D-ACF0-C41D59C69B7A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Providers.RedisCache", "AuthJanitor.Providers.RedisCache\AuthJanitor.Providers.RedisCache.csproj", "{A22F4B5A-10D8-4692-9C34-D7089108E4B5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Providers.AzureSearch", "AuthJanitor.Providers.AzureSearch\AuthJanitor.Providers.AzureSearch.csproj", "{AF99E01C-3AB2-4AF6-8771-871B3F0E380E}" @@ -65,12 +49,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Providers.Azure EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Tests", "AuthJanitor.Tests\AuthJanitor.Tests.csproj", "{4EF08D3E-5F88-4156-8BBA-9FD8A3065190}" 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("{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 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.CLI", "AuthJanitor.CLI\AuthJanitor.CLI.csproj", "{46219CAD-2304-4CB1-821C-97909D358065}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Repository", "AuthJanitor.Repository\AuthJanitor.Repository.csproj", "{04055E50-4794-43D9-BDA7-FCC91F23C8EF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthJanitor.Automation.Blazor", "AuthJanitor.Automation.Blazor\AuthJanitor.Automation.Blazor.csproj", "{EF1C9F16-AF6B-44DD-9D83-DC0E5BD21B9E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -101,30 +89,10 @@ Global {03A38E05-A6D4-4325-A3A4-6AE33D7E6718}.Debug|Any CPU.Build.0 = Debug|Any CPU {03A38E05-A6D4-4325-A3A4-6AE33D7E6718}.Release|Any CPU.ActiveCfg = Release|Any CPU {03A38E05-A6D4-4325-A3A4-6AE33D7E6718}.Release|Any CPU.Build.0 = Release|Any CPU - {8DBA0BBC-F92B-456D-8342-0C42E39EB81E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DBA0BBC-F92B-456D-8342-0C42E39EB81E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DBA0BBC-F92B-456D-8342-0C42E39EB81E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DBA0BBC-F92B-456D-8342-0C42E39EB81E}.Release|Any CPU.Build.0 = Release|Any CPU - {F9DCAB83-056E-4F4B-A8CA-9DDBBCF08394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F9DCAB83-056E-4F4B-A8CA-9DDBBCF08394}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F9DCAB83-056E-4F4B-A8CA-9DDBBCF08394}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F9DCAB83-056E-4F4B-A8CA-9DDBBCF08394}.Release|Any CPU.Build.0 = Release|Any CPU - {5BDB5B18-884B-4BB5-BF20-A06114C5C928}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5BDB5B18-884B-4BB5-BF20-A06114C5C928}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5BDB5B18-884B-4BB5-BF20-A06114C5C928}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5BDB5B18-884B-4BB5-BF20-A06114C5C928}.Release|Any CPU.Build.0 = Release|Any CPU - {1221DBD5-BD6A-482D-B842-AF53611A48A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1221DBD5-BD6A-482D-B842-AF53611A48A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1221DBD5-BD6A-482D-B842-AF53611A48A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1221DBD5-BD6A-482D-B842-AF53611A48A9}.Release|Any CPU.Build.0 = Release|Any CPU {CC3E1903-9A04-4A14-8EDB-59444BB57E05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CC3E1903-9A04-4A14-8EDB-59444BB57E05}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC3E1903-9A04-4A14-8EDB-59444BB57E05}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC3E1903-9A04-4A14-8EDB-59444BB57E05}.Release|Any CPU.Build.0 = Release|Any CPU - {31D82FCB-3F22-482C-B7A4-F9D9D4A27EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {31D82FCB-3F22-482C-B7A4-F9D9D4A27EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31D82FCB-3F22-482C-B7A4-F9D9D4A27EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {31D82FCB-3F22-482C-B7A4-F9D9D4A27EF6}.Release|Any CPU.Build.0 = Release|Any CPU {1E8E5F06-CF36-4CC8-95BD-B7E455D1737C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E8E5F06-CF36-4CC8-95BD-B7E455D1737C}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E8E5F06-CF36-4CC8-95BD-B7E455D1737C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -141,10 +109,6 @@ Global {F103367D-F5FB-43A6-AA52-6B9A0DD66862}.Debug|Any CPU.Build.0 = Debug|Any CPU {F103367D-F5FB-43A6-AA52-6B9A0DD66862}.Release|Any CPU.ActiveCfg = Release|Any CPU {F103367D-F5FB-43A6-AA52-6B9A0DD66862}.Release|Any CPU.Build.0 = Release|Any CPU - {2ED2CBEF-1B5F-4D0D-ACF0-C41D59C69B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2ED2CBEF-1B5F-4D0D-ACF0-C41D59C69B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2ED2CBEF-1B5F-4D0D-ACF0-C41D59C69B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2ED2CBEF-1B5F-4D0D-ACF0-C41D59C69B7A}.Release|Any CPU.Build.0 = Release|Any CPU {A22F4B5A-10D8-4692-9C34-D7089108E4B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A22F4B5A-10D8-4692-9C34-D7089108E4B5}.Debug|Any CPU.Build.0 = Debug|Any CPU {A22F4B5A-10D8-4692-9C34-D7089108E4B5}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -169,14 +133,22 @@ Global {4EF08D3E-5F88-4156-8BBA-9FD8A3065190}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EF08D3E-5F88-4156-8BBA-9FD8A3065190}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EF08D3E-5F88-4156-8BBA-9FD8A3065190}.Release|Any CPU.Build.0 = Release|Any CPU - {BCEC6AD5-4A69-4440-8643-AED247F45281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BCEC6AD5-4A69-4440-8643-AED247F45281}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BCEC6AD5-4A69-4440-8643-AED247F45281}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BCEC6AD5-4A69-4440-8643-AED247F45281}.Release|Any CPU.Build.0 = Release|Any CPU {F5F9C392-D02D-4501-BE85-8DDAAC5D06F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5F9C392-D02D-4501-BE85-8DDAAC5D06F3}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5F9C392-D02D-4501-BE85-8DDAAC5D06F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5F9C392-D02D-4501-BE85-8DDAAC5D06F3}.Release|Any CPU.Build.0 = Release|Any CPU + {46219CAD-2304-4CB1-821C-97909D358065}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46219CAD-2304-4CB1-821C-97909D358065}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46219CAD-2304-4CB1-821C-97909D358065}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46219CAD-2304-4CB1-821C-97909D358065}.Release|Any CPU.Build.0 = Release|Any CPU + {04055E50-4794-43D9-BDA7-FCC91F23C8EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04055E50-4794-43D9-BDA7-FCC91F23C8EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04055E50-4794-43D9-BDA7-FCC91F23C8EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04055E50-4794-43D9-BDA7-FCC91F23C8EF}.Release|Any CPU.Build.0 = Release|Any CPU + {EF1C9F16-AF6B-44DD-9D83-DC0E5BD21B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF1C9F16-AF6B-44DD-9D83-DC0E5BD21B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF1C9F16-AF6B-44DD-9D83-DC0E5BD21B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF1C9F16-AF6B-44DD-9D83-DC0E5BD21B9E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -189,28 +161,22 @@ Global {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} - {1221DBD5-BD6A-482D-B842-AF53611A48A9} = {4AB5E96D-1A78-44E0-A52B-5F1A7ABE3B40} - {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} {9A5610CB-A46E-4542-A4F4-1CACBA12E171} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0} {AF18A358-231F-4D8E-A659-AEB660CC4715} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0} {93A3274C-6CD8-4399-9C45-7D0A4077E4E0} = {9C0E454F-146E-40FB-AFE5-F63DAACAB326} {7EDE5077-75D1-4D60-85AA-94E1F99525EC} = {93A3274C-6CD8-4399-9C45-7D0A4077E4E0} - {BCEC6AD5-4A69-4440-8643-AED247F45281} = {07EE807A-D975-4B1D-B486-6633ED87B757} {F5F9C392-D02D-4501-BE85-8DDAAC5D06F3} = {07EE807A-D975-4B1D-B486-6633ED87B757} + {46219CAD-2304-4CB1-821C-97909D358065} = {C8572CCA-36EC-4AAB-A4C0-AF23B4B82B8C} + {04055E50-4794-43D9-BDA7-FCC91F23C8EF} = {C8572CCA-36EC-4AAB-A4C0-AF23B4B82B8C} + {EF1C9F16-AF6B-44DD-9D83-DC0E5BD21B9E} = {45A0E86E-0F1A-434E-B3F9-D9AE88C964E1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0461C5FC-1885-4C0F-B202-817D99C927D2}