зеркало из https://github.com/Azure/Sia-Root.git
Merge pull request #6 from Azure/pdimit/DomainInRoot
Added domain and shared logic to root for use as submodule
This commit is contained in:
Коммит
46ad89eb58
|
@ -0,0 +1,10 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sia.Domain.ApiModels
|
||||
{
|
||||
public class NewEngagement
|
||||
{
|
||||
[Required]
|
||||
public Participant Participant { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Dynamic;
|
||||
|
||||
namespace Sia.Domain.ApiModels
|
||||
{
|
||||
public class NewEvent
|
||||
:IJsonDataObject
|
||||
{
|
||||
[Required]
|
||||
public long? EventTypeId { get; set; }
|
||||
[Required]
|
||||
public DateTime? Occurred { get; set; }
|
||||
[Required]
|
||||
public DateTime? EventFired { get; set; }
|
||||
public object Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sia.Domain.ApiModels
|
||||
{
|
||||
public class NewIncident
|
||||
{
|
||||
[Required]
|
||||
public string Title { get; set; }
|
||||
[Required]
|
||||
public Ticket PrimaryTicket
|
||||
{
|
||||
get
|
||||
{
|
||||
return Tickets.FirstOrDefault(ticket => ticket.IsPrimary);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Tickets == null) Tickets = new List<Ticket>();
|
||||
foreach (var ticket in Tickets.Where(ticket => ticket.IsPrimary))
|
||||
{
|
||||
ticket.IsPrimary = false;
|
||||
}
|
||||
|
||||
if (value == null) return;
|
||||
|
||||
if (!Tickets.Contains(value)) Tickets.Add(value);
|
||||
value.IsPrimary = true;
|
||||
}
|
||||
}
|
||||
public IList<Ticket> Tickets { get; set; }
|
||||
= new List<Ticket>();
|
||||
public IList<NewEvent> Events { get; set; }
|
||||
= new List<NewEvent>();
|
||||
public IList<Participant> Engagements { get; set; }
|
||||
= new List<Participant>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Sia.Domain.Playbook;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateAction
|
||||
{
|
||||
public CreateActionTemplate NewActionTemplate { get; set; }
|
||||
public long? ExistingActionTemplateId { get; set; }
|
||||
public ICollection<CreateConditionSet> NewConditionSets { get; set; }
|
||||
= new List<CreateConditionSet>();
|
||||
public ICollection<long> ExistingConditionSetIds { get; set; }
|
||||
= new List<long>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateActionTemplate
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
public bool IsUrl { get; set; }
|
||||
public string Template { get; set; }
|
||||
public ICollection<CreateActionTemplateSource> NewSources { get; set; }
|
||||
= new List<CreateActionTemplateSource>();
|
||||
public ICollection<long> ExistingSourceIds { get; set; }
|
||||
= new List<long>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateActionTemplateSource : CreateSource
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using Sia.Domain.Playbook;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateCondition
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public AssertionType AssertionType { get; set; }
|
||||
= AssertionType.IsOrDoes;
|
||||
public ConditionType ConditionType { get; set; }
|
||||
public DataFormat DataFormat { get; set; }
|
||||
= DataFormat.String;
|
||||
public string ComparisonValue { get; set; }
|
||||
public long? IntegerComparisonValue { get; set; }
|
||||
public DateTime DateTimeComparisonValue { get; set; }
|
||||
public long? ExistingConditionSourceId { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Sia.Domain.Playbook;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateConditionSet
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ConditionSetType Type { get; set; }
|
||||
public ICollection<CreateCondition> NewConditions { get; set; }
|
||||
= new List<CreateCondition>();
|
||||
public ICollection<long> ExistingConditionIds { get; set; }
|
||||
= new List<long>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateConditionSource : CreateSource
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public class CreateEventType : IJsonDataObject
|
||||
{
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
public object Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using Sia.Domain.Playbook;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.ApiModels.Playbooks
|
||||
{
|
||||
public abstract class CreateSource
|
||||
{
|
||||
public SourceObject SourceObject { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageId>Microsoft.Sia.Domain.ApiModels</PackageId>
|
||||
<Version>1.1.1-alpha</Version>
|
||||
<Authors>pdimit, magpint, jache, chtownes</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
<Product>SRE Incident Assistant</Product>
|
||||
<Description>API operation models for the SRE Incident Assistant</Description>
|
||||
<Copyright>Copyright 2017</Copyright>
|
||||
<PackageReleaseNotes>Initial open source release, intended for prototyping.</PackageReleaseNotes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sia.Domain\Sia.Domain.csproj" />
|
||||
<ProjectReference Include="..\Sia.Shared\Sia.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Sia.Domain.ApiModels
|
||||
{
|
||||
public class UpdateEngagement
|
||||
{
|
||||
public DateTime? TimeEngaged { get; set; }
|
||||
public DateTime? TimeDisengaged { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using Sia.Shared.Data;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Sia.Domain.ApiModels
|
||||
{
|
||||
public class UpdateIncident
|
||||
{
|
||||
public string Title { get; set; }
|
||||
[Required]
|
||||
public Ticket PrimaryTicket { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2005
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sia.Domain", "Sia.Domain\Sia.Domain.csproj", "{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sia.Domain.ApiModels", "Sia.Domain.ApiModels\Sia.Domain.ApiModels.csproj", "{23F482C9-DBFE-4E14-A128-BF78B9FC8702}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sia.Shared", "Sia.Shared\Sia.Shared.csproj", "{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5E87ABD1-9ABF-4E97-9FE1-7CF015B72CD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23F482C9-DBFE-4E14-A128-BF78B9FC8702}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1EEED92C-7BD2-4F23-9C04-1D8638EE1564}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C4632DD2-4898-45B0-B3C5-FCBA12370F09}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,16 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain
|
||||
{
|
||||
public class Engagement : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long IncidentId { get; set; }
|
||||
public DateTime TimeEngaged { get; set; }
|
||||
public DateTime? TimeDisengaged { get; set; }
|
||||
public Participant Participant {get; set;}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using Sia.Domain;
|
||||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain
|
||||
{
|
||||
public class Event : IEntity, IJsonDataObject
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long? IncidentId { get; set; }
|
||||
public long EventTypeId { get; set; }
|
||||
public DateTime Occurred { get; set; }
|
||||
public DateTime EventFired { get; set; }
|
||||
public object Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using Sia.Shared.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sia.Domain
|
||||
{
|
||||
public class Incident : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Title { get; set; }
|
||||
|
||||
public Ticket PrimaryTicket
|
||||
{
|
||||
get
|
||||
{
|
||||
return Tickets.FirstOrDefault(ticket => ticket.IsPrimary);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Tickets == null) Tickets = new List<Ticket>();
|
||||
foreach (var ticket in Tickets.Where(ticket => ticket.IsPrimary))
|
||||
{
|
||||
ticket.IsPrimary = false;
|
||||
}
|
||||
|
||||
if (value == null) return;
|
||||
|
||||
if (!Tickets.Contains(value)) Tickets.Add(value);
|
||||
value.IsPrimary = true;
|
||||
}
|
||||
}
|
||||
public ICollection<Ticket> Tickets { get; set; }
|
||||
= new List<Ticket>();
|
||||
public ICollection<Event> Events { get; set; }
|
||||
= new List<Event>();
|
||||
public ICollection<Engagement> Engagements { get; set; }
|
||||
= new List<Engagement>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace Sia.Domain
|
||||
{
|
||||
public class Participant
|
||||
{
|
||||
public string Alias { get; set; }
|
||||
public string Team { get; set; }
|
||||
public string Role { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class Action : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public ActionTemplate ActionTemplate { get; set; }
|
||||
public ICollection<ConditionSet> ConditionSets { get; set; }
|
||||
= new List<ConditionSet>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class ActionTemplate : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool IsUrl { get; set; }
|
||||
public string Template { get; set; }
|
||||
public ICollection<ActionTemplateSource> Sources { get; set; }
|
||||
= new List<ActionTemplateSource>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class ActionTemplateSource : Source
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class Condition : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public AssertionType AssertionType { get; set; }
|
||||
= AssertionType.IsOrDoes;
|
||||
public ConditionType ConditionType { get; set; }
|
||||
public DataFormat DataFormat { get; set; }
|
||||
= DataFormat.String;
|
||||
public string ComparisonValue { get; set; }
|
||||
public long? IntegerComparisonValue { get; set; }
|
||||
public DateTime DateTimeComparisonValue { get; set; }
|
||||
public ConditionSource ConditionSource { get; set; }
|
||||
}
|
||||
|
||||
public enum AssertionType
|
||||
{
|
||||
IsOrDoes,
|
||||
IsNotOrDoesNot
|
||||
}
|
||||
|
||||
public enum ConditionType
|
||||
{
|
||||
Equal,
|
||||
Contain,
|
||||
HaveValue,
|
||||
GreaterThan,
|
||||
LessThan
|
||||
}
|
||||
|
||||
public enum DataFormat
|
||||
{
|
||||
String,
|
||||
DateTime,
|
||||
Integer
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class ConditionSet : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public ConditionSetType Type { get; set; }
|
||||
public ICollection<Condition> Conditions { get; set; }
|
||||
= new List<Condition>();
|
||||
}
|
||||
|
||||
public enum ConditionSetType
|
||||
{
|
||||
AnyOf,
|
||||
AllOf,
|
||||
NoneOf
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class ConditionSource : Source
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using Sia.Shared.Data;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public class EventType : IEntity, IJsonDataObject
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public object Data { get; set; }
|
||||
public ICollection<Action> Actions { get; set; }
|
||||
= new List<Action>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Domain.Playbook
|
||||
{
|
||||
public abstract class Source : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public SourceObject SourceObject { get; set; }
|
||||
public string Key { get; set; }
|
||||
}
|
||||
|
||||
public enum SourceObject
|
||||
{
|
||||
Event,
|
||||
Ticket,
|
||||
EventType,
|
||||
Engagement
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>Microsoft.Sia.Domain</PackageId>
|
||||
<Version>1.1.3-alpha</Version>
|
||||
<Authors>pdimit, magpint, jache, chtownes</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
<Product>SRE Incident Assistant</Product>
|
||||
<Description>Domain models for the SRE Incident Assistant</Description>
|
||||
<Copyright>Copyright 2017</Copyright>
|
||||
<PackageReleaseNotes>Initial open source release, intended for prototyping.</PackageReleaseNotes>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sia.Shared\Sia.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,14 @@
|
|||
using Sia.Shared.Data;
|
||||
|
||||
namespace Sia.Domain
|
||||
{
|
||||
public class Ticket : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long IncidentId { get; set; }
|
||||
public string OriginId { get; set; }
|
||||
public long TicketingSystemId { get; set; }
|
||||
public string OriginUri { get; set; }
|
||||
public bool IsPrimary { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
using Sia.Shared.Data;
|
||||
|
||||
namespace Sia.Domain
|
||||
{
|
||||
public class TicketingSystem : IEntity
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
using Microsoft.Azure.KeyVault;
|
||||
using Microsoft.Azure.KeyVault.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Clients.ActiveDirectory;
|
||||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public interface ISecretVault
|
||||
{
|
||||
Task<string> Get(string secretName);
|
||||
}
|
||||
public class AzureSecretVault : ISecretVault
|
||||
{
|
||||
private readonly KeyVaultConfiguration _config;
|
||||
private const string _secretsEndpoint = "/secrets/";
|
||||
private const string _keysEndpoint = "/keys/";
|
||||
private const string _certificatesEndpoint = "/certificates/";
|
||||
|
||||
public AzureSecretVault(KeyVaultConfiguration configuration)
|
||||
{
|
||||
_config = ThrowIf.Null(configuration, nameof(configuration));
|
||||
}
|
||||
|
||||
public async Task<string> Get(string secretName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var secret = await GetKeyVaultClient().GetSecretAsync(_config.Vault + _secretsEndpoint + secretName).ConfigureAwait(false);
|
||||
return secret.Value;
|
||||
}
|
||||
catch (KeyVaultErrorException ex)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<X509Certificate2> GetCertificate(string certificateName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cert = await GetKeyVaultClient()
|
||||
.GetCertificateAsync(_config.Vault, certificateName)
|
||||
.ConfigureAwait(false);
|
||||
return new X509Certificate2(cert.Cer);
|
||||
}
|
||||
catch (KeyVaultErrorException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetToken(string authority, string resource, string scope)
|
||||
{
|
||||
var authContext = new AuthenticationContext(authority);
|
||||
ClientCredential clientCred = new ClientCredential(_config.ClientId, _config.ClientSecret);
|
||||
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
|
||||
|
||||
if (result == null)
|
||||
throw new InvalidOperationException("Failed to obtain the JWT token");
|
||||
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
private KeyVaultClient GetKeyVaultClient() => new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System.Net.Http;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public abstract class CertificateRetriever : IHttpClientFactory
|
||||
{
|
||||
protected X509Certificate2 _cert;
|
||||
|
||||
public abstract X509Certificate2 Certificate { get; }
|
||||
|
||||
public virtual HttpClient GetClient()
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
handler.ClientCertificates.Add(Certificate);
|
||||
|
||||
return new HttpClient(handler);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
using System.Net.Http;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public interface IHttpClientFactory
|
||||
{
|
||||
HttpClient GetClient();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public class KeyVaultCertificateRetriever
|
||||
: CertificateRetriever
|
||||
{
|
||||
private readonly X509Certificate2 _certificate;
|
||||
|
||||
public KeyVaultCertificateRetriever(AzureSecretVault certificateVault, string certificateName)
|
||||
{
|
||||
ThrowIf.NullOrWhiteSpace(certificateName, nameof(certificateName));
|
||||
|
||||
var certTask = certificateVault.GetCertificate(certificateName);
|
||||
Task.WaitAll(new Task[] { certTask });
|
||||
if (certTask.IsCompleted)
|
||||
{
|
||||
_certificate = certTask.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public override X509Certificate2 Certificate => _certificate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public class KeyVaultConfiguration
|
||||
{
|
||||
public KeyVaultConfiguration(string clientId, string clientSecret, string vault)
|
||||
{
|
||||
ClientId = ThrowIf.NullOrWhiteSpace(clientId, nameof(clientId));
|
||||
ClientSecret = ThrowIf.NullOrWhiteSpace(clientSecret, nameof(clientSecret));
|
||||
Vault = String.Format(secretUriBase, ThrowIf.NullOrWhiteSpace(vault, nameof(vault)));
|
||||
}
|
||||
|
||||
private const string secretUriBase = "https://{0}.vault.azure.net";
|
||||
|
||||
public readonly string Vault;
|
||||
public readonly string ClientId;
|
||||
public readonly string ClientSecret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public class LocalCertificateRetriever
|
||||
: CertificateRetriever
|
||||
{
|
||||
private readonly string _certThumbprint;
|
||||
private List<StoreLocation> _storeLocations
|
||||
=> new List<StoreLocation>
|
||||
{
|
||||
StoreLocation.CurrentUser,
|
||||
StoreLocation.LocalMachine
|
||||
};
|
||||
|
||||
public LocalCertificateRetriever(string certThumbprint)
|
||||
{
|
||||
_certThumbprint = certThumbprint;
|
||||
}
|
||||
|
||||
|
||||
public override X509Certificate2 Certificate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cert == null)
|
||||
{
|
||||
_cert = GetCertFromStore();
|
||||
}
|
||||
return _cert;
|
||||
}
|
||||
}
|
||||
|
||||
private X509Certificate2 GetCertFromStore()
|
||||
{
|
||||
foreach (var location in _storeLocations)
|
||||
{
|
||||
using (X509Store store = new X509Store("My", location))
|
||||
{
|
||||
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
|
||||
X509Certificate2Collection certificates = store.Certificates.Find(X509FindType.FindByThumbprint, _certThumbprint, true);
|
||||
|
||||
if (certificates.Count > 0)
|
||||
return certificates[0];
|
||||
}
|
||||
}
|
||||
throw new KeyNotFoundException("Could not find valid certificate");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System.Net.Http;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public class UnauthenticatedClientFactory : IHttpClientFactory
|
||||
{
|
||||
HttpClient _httpClient;
|
||||
public HttpClient GetClient()
|
||||
{
|
||||
if (_httpClient == null)
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
return _httpClient;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Shared.Data
|
||||
{
|
||||
public interface IAssociation<TLeft, TRight>
|
||||
{
|
||||
void Associate(TLeft left, TRight right);
|
||||
KeyValuePair<TLeft, TRight> AssociatesBetween();
|
||||
}
|
||||
|
||||
public abstract class Association<TLeft, TRight>
|
||||
: IAssociation<TLeft, TRight>
|
||||
where TLeft : IEntity
|
||||
where TRight : IEntity
|
||||
{
|
||||
protected internal abstract long _leftId { get; set; }
|
||||
protected internal abstract long _rightId { get; set; }
|
||||
protected internal abstract TLeft _left { get; set; }
|
||||
protected internal abstract TRight _right { get; set; }
|
||||
public void Associate(TLeft left, TRight right)
|
||||
{
|
||||
_left = left;
|
||||
_right = right;
|
||||
_leftId = left.Id;
|
||||
_rightId = right.Id;
|
||||
}
|
||||
|
||||
public KeyValuePair<TLeft, TRight> AssociatesBetween()
|
||||
=> new KeyValuePair<TLeft, TRight>(_left, _right);
|
||||
}
|
||||
|
||||
public abstract class BidrectionalAssociation<TLeft, TRight>
|
||||
: Association<TLeft, TRight>,
|
||||
IAssociation<TRight, TLeft>
|
||||
where TLeft : IEntity
|
||||
where TRight : IEntity
|
||||
{
|
||||
protected BidrectionalAssociation()
|
||||
{
|
||||
}
|
||||
|
||||
void IAssociation<TRight, TLeft>.Associate(TRight left, TLeft right)
|
||||
=> Associate(right, left);
|
||||
|
||||
|
||||
KeyValuePair<TRight, TLeft> IAssociation<TRight, TLeft>.AssociatesBetween()
|
||||
=> new KeyValuePair<TRight, TLeft>(_right, _left);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Shared.Data
|
||||
{
|
||||
public interface IEntity
|
||||
{
|
||||
long Id { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Sia.Shared.Data
|
||||
{
|
||||
public class ManyToManyCollection<TItem, TAssociation, TAssociated> : ICollection<TAssociated>
|
||||
where TAssociation : IAssociation<TItem, TAssociated>, new()
|
||||
{
|
||||
private TItem _parent;
|
||||
private ICollection<TAssociation> _associations;
|
||||
private IEnumerable<TAssociated> _associated => _associations.Select(assoc => assoc.AssociatesBetween().Value);
|
||||
|
||||
public ManyToManyCollection(TItem parent, ICollection<TAssociation> associations)
|
||||
{
|
||||
_parent = parent;
|
||||
_associations = associations;
|
||||
}
|
||||
|
||||
public int Count => _associations.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public void Add(TAssociated item)
|
||||
{
|
||||
var associationToAdd = new TAssociation();
|
||||
associationToAdd.Associate(_parent, item);
|
||||
_associations.Add(associationToAdd);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_associations.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(TAssociated item)
|
||||
{
|
||||
return _associated.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(TAssociated[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null) throw new ArgumentNullException(nameof(arrayIndex));
|
||||
if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, "Array Index cannot be less than 0!");
|
||||
if (array.GetLength(0) - arrayIndex > Count) throw new ArgumentException("The number of elements in the source System.Collections.Generic.ICollection`1 is greater than the available space from arrayIndex to the end of the destination array.", nameof(arrayIndex));
|
||||
|
||||
_associated.ToList().CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public IEnumerator<TAssociated> GetEnumerator()
|
||||
{
|
||||
return _associated.GetEnumerator();
|
||||
}
|
||||
|
||||
public bool Remove(TAssociated item)
|
||||
{
|
||||
var relevantAssociations = _associations.Where(assoc => assoc.AssociatesBetween().Value.Equals(item));
|
||||
bool success = true;
|
||||
foreach (var association in relevantAssociations)
|
||||
{
|
||||
if (!_associations.Remove(association))
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _associated.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using Sia.Shared.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.EntityFrameworkCore
|
||||
{
|
||||
public static class ModelBuilderExtensions
|
||||
{
|
||||
public static void WithManyToManyAssociation<TLeft, TRight, TAssociation>(this ModelBuilder builder
|
||||
, Expression<Func<TLeft, IEnumerable<TAssociation>>> associationsFromLeft
|
||||
, Expression<Func<TRight, IEnumerable<TAssociation>>> associationsFromRight
|
||||
)
|
||||
where TAssociation : BidrectionalAssociation<TLeft, TRight>
|
||||
where TLeft : class, IEntity
|
||||
where TRight : class, IEntity
|
||||
{
|
||||
builder.Entity<TAssociation>()
|
||||
.HasOne(assoc => assoc._left)
|
||||
.WithMany(associationsFromLeft)
|
||||
.HasForeignKey(assoc => assoc._leftId);
|
||||
builder.Entity<TAssociation>()
|
||||
.HasOne(assoc => assoc._right)
|
||||
.WithMany(associationsFromRight)
|
||||
.HasForeignKey(assoc => assoc._rightId);
|
||||
builder.Entity<TAssociation>()
|
||||
.HasKey(assoc => new { assoc._leftId, assoc._rightId });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using AutoMapper;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Shared.Data
|
||||
{
|
||||
|
||||
public interface IJsonDataString
|
||||
{
|
||||
string Data { get; set; }
|
||||
}
|
||||
|
||||
public interface IJsonDataObject
|
||||
{
|
||||
object Data { get; set; }
|
||||
}
|
||||
|
||||
public class ResolveJsonToString<TSource, TDestination>
|
||||
: IValueResolver<TSource, TDestination, string>
|
||||
where TSource: IJsonDataObject
|
||||
where TDestination: IJsonDataString
|
||||
{
|
||||
public string Resolve(TSource source, TDestination destination, string destMember, ResolutionContext context)
|
||||
=> source.Data is null ? null : JsonConvert.SerializeObject(source.Data);
|
||||
}
|
||||
|
||||
public class ResolveStringToJson<TSource, TDestination>
|
||||
: IValueResolver<TSource, TDestination, object>
|
||||
where TSource : IJsonDataString
|
||||
where TDestination : IJsonDataObject
|
||||
{
|
||||
public object Resolve(TSource source, TDestination destination, object destMember, ResolutionContext context)
|
||||
=> source.Data is null ? null : JsonConvert.DeserializeObject(source.Data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Sia.Shared.Exceptions
|
||||
{
|
||||
public class BadRequestException : BaseException
|
||||
{
|
||||
public BadRequestException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public override int StatusCode => 400;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Sia.Shared.Exceptions
|
||||
{
|
||||
public abstract class BaseException : Exception
|
||||
{
|
||||
public BaseException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract int StatusCode { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Sia.Shared.Exceptions
|
||||
{
|
||||
public class ConflictException : BaseException
|
||||
{
|
||||
public ConflictException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public override int StatusCode => 409;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using Sia.Shared.Transactions;
|
||||
using System;
|
||||
|
||||
namespace Sia.Shared.Exceptions
|
||||
{
|
||||
public static class ConvertStatusToException
|
||||
{
|
||||
public static void ThrowExceptionOnUnsuccessfulStatus<T>(this IResponse<T> response)
|
||||
{
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var message = response.Content;
|
||||
switch (response.StatusCode)
|
||||
{
|
||||
case System.Net.HttpStatusCode.BadRequest:
|
||||
throw new BadRequestException(message);
|
||||
case System.Net.HttpStatusCode.Conflict:
|
||||
throw new ConflictException(message);
|
||||
case System.Net.HttpStatusCode.Forbidden:
|
||||
throw new UnauthorizedException(message);
|
||||
case System.Net.HttpStatusCode.NotFound:
|
||||
throw new NotFoundException(message);
|
||||
case System.Net.HttpStatusCode.Unauthorized:
|
||||
throw new UnauthorizedException(message);
|
||||
default:
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Sia.Shared.Exceptions
|
||||
{
|
||||
public class NotFoundException : BaseException
|
||||
{
|
||||
public NotFoundException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public override int StatusCode => 404;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace Sia.Shared.Exceptions
|
||||
{
|
||||
public class UnauthorizedException : BaseException
|
||||
{
|
||||
public UnauthorizedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public override int StatusCode => 403;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>1.1.0-alpha</Version>
|
||||
<PackageId>Microsoft.Sia.Shared</PackageId>
|
||||
<Authors>pdimit, magpint, jache, chtownes</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
<Product>SRE Incident Assistant</Product>
|
||||
<Description>SIA utility and shared code</Description>
|
||||
<PackageReleaseNotes>Initial open source release, intended for prototyping.</PackageReleaseNotes>
|
||||
<Copyright>Copyright 2017</Copyright>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||
<PackageReference Include="Microsoft.Azure.KeyVault" Version="2.3.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.16.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
using System.Net;
|
||||
|
||||
namespace Sia.Shared.Transactions
|
||||
{
|
||||
public interface IResponse<T>
|
||||
{
|
||||
HttpStatusCode StatusCode { get; }
|
||||
bool IsSuccessStatusCode { get; }
|
||||
string Content { get; }
|
||||
T Value { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Shared.Transactions
|
||||
{
|
||||
public class Response<T> : IResponse<T>
|
||||
{
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
public bool IsSuccessStatusCode { get; private set; }
|
||||
public string Content { get; private set; }
|
||||
public T Value { get; private set; }
|
||||
public static async Task<Response<T>> Create(HttpResponseMessage message)
|
||||
{
|
||||
var response = new Response<T>();
|
||||
response.IsSuccessStatusCode = message.IsSuccessStatusCode;
|
||||
response.StatusCode = message.StatusCode;
|
||||
var content = await message.Content.ReadAsStringAsync();
|
||||
if (message.IsSuccessStatusCode)
|
||||
{
|
||||
response.Value = JsonConvert.DeserializeObject<T>(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Content = content;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
|
||||
namespace Sia.Shared.Validation
|
||||
{
|
||||
public static class ThrowIf
|
||||
{
|
||||
public static TParam Null<TParam>(TParam o, string paramName)
|
||||
where TParam : class
|
||||
=> o ?? throw new ArgumentNullException(paramName);
|
||||
|
||||
public static string NullOrWhiteSpace(string input, string paramName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input)) throw new ArgumentException("Value cannot be null or white space", paramName);
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче