Skip to content

Commit 2c9924f

Browse files
Merge pull request #340 from microsoft/dev
support for github environments
2 parents cfab301 + 2b546ca commit 2c9924f

File tree

13 files changed

+339
-182
lines changed

13 files changed

+339
-182
lines changed

src/TeamCloud.Adapters.AzureDevOps/AzureDevOpsAdapter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public sealed class AzureDevOpsAdapter : AdapterWithIdentity, IAdapterAuthorize
7979
// however; it is used to managed singleton function execution within the functions fx !!!
8080

8181
public AzureDevOpsAdapter(
82+
IAdapterProvider adapterProvider,
8283
IAuthorizationSessionClient sessionClient,
8384
IAuthorizationTokenClient tokenClient,
8485
IDistributedLockManager distributedLockManager,
@@ -94,7 +95,7 @@ public AzureDevOpsAdapter(
9495
IGraphService graphService,
9596
IFunctionsHost functionsHost = null,
9697
ILoggerFactory loggerFactory = null)
97-
: base(sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
98+
: base(adapterProvider, sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
9899
{
99100
this.httpClientFactory = httpClientFactory ?? new DefaultHttpClientFactory();
100101
this.userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository));

src/TeamCloud.Adapters.AzureResourceManager/AzureResourceManagerAdapter.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
namespace TeamCloud.Adapters.AzureResourceManager;
2626

27-
public sealed class AzureResourceManagerAdapter : Adapter
27+
public sealed class AzureResourceManagerAdapter : AdapterWithIdentity
2828
{
2929
private readonly IAzureService azure;
3030
private readonly IAzureResourceService azureResourceService;
@@ -40,7 +40,9 @@ public sealed class AzureResourceManagerAdapter : Adapter
4040
// IDistributedLockManager is marked as obsolete, because it's not ready for "prime time"
4141
// however; it is used to managed singleton function execution within the functions fx !!!
4242

43-
public AzureResourceManagerAdapter(IAuthorizationSessionClient sessionClient,
43+
public AzureResourceManagerAdapter(
44+
IAdapterProvider adapterProvider,
45+
IAuthorizationSessionClient sessionClient,
4446
IAuthorizationTokenClient tokenClient,
4547
IDistributedLockManager distributedLockManager,
4648
IAzureService azure,
@@ -52,7 +54,7 @@ public AzureResourceManagerAdapter(IAuthorizationSessionClient sessionClient,
5254
IProjectRepository projectRepository,
5355
IComponentRepository componentRepository,
5456
IComponentTemplateRepository componentTemplateRepository)
55-
: base(sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
57+
: base(adapterProvider, sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
5658
{
5759
this.azure = azure ?? throw new ArgumentNullException(nameof(azure));
5860
this.azureResourceService = azureResourceService ?? throw new ArgumentNullException(nameof(azureResourceService));
@@ -197,6 +199,16 @@ async Task UpdateComponentRoleAssignmentsAsync()
197199
.Add(identity.PrincipalId, Enumerable.Repeat(AzureRoleDefinition.Contributor, 1));
198200
}
199201

202+
if (this is IAdapterIdentity adapterIdentity)
203+
{
204+
var componentDeploymentScopeServiceIdentity = await adapterIdentity
205+
.GetServiceIdentityAsync(componentDeploymentScope)
206+
.ConfigureAwait(false);
207+
208+
roleAssignmentMap
209+
.Add(componentDeploymentScopeServiceIdentity.Id.ToString(), Enumerable.Repeat(AzureRoleDefinition.Contributor, 1));
210+
}
211+
200212
if (string.IsNullOrEmpty(componentResourceId.ResourceGroup))
201213
{
202214
var subscription = await azureResourceService

src/TeamCloud.Adapters.GitHub/GitHubAdapter.cs

Lines changed: 275 additions & 145 deletions
Large diffs are not rendered by default.

src/TeamCloud.Adapters.GitHub/GitHubAdapter_Register.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ <h1>GitHub Provider Setup</h1>
153153
"default_permissions": {
154154
"actions": "write",
155155
"administration": "write",
156+
"environments": "write",
156157
"checks": "write",
157158
"contents": "write",
158159
"issues": "write",

src/TeamCloud.Adapters.GitHub/GitHubData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public sealed class GitHubData
1515
[TeamCloudFormDescription("GitHub organization's name or base URL.")]
1616
public string Organization
1717
{
18-
get => string.IsNullOrWhiteSpace(organization) ? null : GitHubToken.FormatOrganizationUrl(organization);
18+
get => string.IsNullOrWhiteSpace(organization) ? null : GitHubToken.SanitizeOrganizationUrl(organization);
1919
set => organization = value;
2020
}
2121

src/TeamCloud.Adapters.GitHub/GitHubToken.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using System;
77
using System.Runtime.Serialization;
8+
using Newtonsoft.Json;
89
using TeamCloud.Adapters.Authorization;
910
using TeamCloud.Model.Data;
1011
using User = Octokit.User;
@@ -13,7 +14,7 @@ namespace TeamCloud.Adapters.GitHub;
1314

1415
public sealed class GitHubToken : AuthorizationToken
1516
{
16-
internal static string FormatOrganizationUrl(string organization)
17+
internal static string SanitizeOrganizationUrl(string organization)
1718
{
1819
if (string.IsNullOrWhiteSpace(organization))
1920
return null;
@@ -39,27 +40,23 @@ public GitHubToken(DeploymentScope deployementScope) : base(GetEntityId(deployem
3940
public bool Enabled
4041
=> !Suspended && long.TryParse(ApplicationId, out _) && long.TryParse(InstallationId, out _);
4142

42-
[DataMember(Name = "id")]
4343
public string ApplicationId { get; set; }
4444

4545
public string InstallationId { get; set; }
4646

47-
[DataMember(Name = "client_id")]
4847
public string ClientId { get; set; }
4948

50-
[DataMember(Name = "client_secret")]
5149
public string ClientSecret { get; set; }
5250

5351
public string AccessToken { get; set; }
5452

5553
// [IgnoreDataMember]
5654
public DateTime? AccessTokenExpires { get; set; }
5755

58-
[IgnoreDataMember]
56+
[JsonIgnore]
5957
public bool AccessTokenExpired
6058
=> !AccessTokenExpires.HasValue || AccessTokenExpires < DateTime.UtcNow;
6159

62-
[DataMember(Name = "webhook_secret")]
6360
public string WebhookSecret { get; set; }
6461

6562
public string Pem { get; set; }

src/TeamCloud.Adapters.GitHub/TeamCloud.Adapters.GitHub.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<PackageReference Include="FluentValidation" Version="9.2.2" />
2626
<PackageReference Include="Flurl.Http" Version="3.2.2" />
2727
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
28-
<PackageReference Include="Octokit" Version="0.50.0" />
28+
<PackageReference Include="Octokit" Version="0.51.0" />
2929
<PackageReference Include="Sodium.Core" Version="1.2.3" />
3030
</ItemGroup>
3131

src/TeamCloud.Adapters.Kubernetes/KubernetesAdapter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public sealed class KubernetesAdapter : Adapter
3838
// IDistributedLockManager is marked as obsolete, because it's not ready for "prime time"
3939
// however; it is used to managed singleton function execution within the functions fx !!!
4040

41-
public KubernetesAdapter(IAuthorizationSessionClient sessionClient,
41+
public KubernetesAdapter(IAdapterProvider adapterProvider,
42+
IAuthorizationSessionClient sessionClient,
4243
IAuthorizationTokenClient tokenClient,
4344
IDistributedLockManager distributedLockManager,
4445
IAzureService azure,
@@ -48,7 +49,7 @@ public KubernetesAdapter(IAuthorizationSessionClient sessionClient,
4849
IDeploymentScopeRepository deploymentScopeRepository,
4950
IProjectRepository projectRepository,
5051
IUserRepository userRepository)
51-
: base(sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
52+
: base(adapterProvider, sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
5253
{
5354
this.azureResourceService = azureResourceService ?? throw new ArgumentNullException(nameof(azureResourceService));
5455
}

src/TeamCloud.Adapters/Adapter.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public abstract class Adapter : IAdapter
2727
{
2828
private static readonly JSchema dataSchemaEmpty = new() { Type = JSchemaType.Object };
2929
private static readonly JObject formSchemaEmpty = new();
30+
private readonly IAdapterProvider adapterProvider;
3031

3132
#pragma warning disable CS0618 // Type or member is obsolete
3233

@@ -43,7 +44,8 @@ public abstract class Adapter : IAdapter
4344
private readonly IProjectRepository projectRepository;
4445
private readonly IUserRepository userRepository;
4546

46-
protected Adapter(IAuthorizationSessionClient sessionClient,
47+
protected Adapter(IAdapterProvider adapterProvider,
48+
IAuthorizationSessionClient sessionClient,
4749
IAuthorizationTokenClient tokenClient,
4850
IDistributedLockManager distributedLockManager,
4951
IAzureService azure,
@@ -53,6 +55,7 @@ protected Adapter(IAuthorizationSessionClient sessionClient,
5355
IProjectRepository projectRepository,
5456
IUserRepository userRepository)
5557
{
58+
this.adapterProvider = adapterProvider ?? throw new ArgumentNullException(nameof(adapterProvider));
5659
this.sessionClient = sessionClient ?? throw new ArgumentNullException(nameof(sessionClient));
5760
this.tokenClient = tokenClient ?? throw new ArgumentNullException(nameof(tokenClient));
5861
this.distributedLockManager = distributedLockManager ?? throw new ArgumentNullException(nameof(distributedLockManager));

src/TeamCloud.Adapters/AdapterWithIdentity.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,37 @@ namespace TeamCloud.Adapters;
1717

1818
public abstract class AdapterWithIdentity : Adapter, IAdapterIdentity
1919
{
20-
private readonly IAzureService azure;
20+
private readonly IAzureService azureService;
2121
private readonly IGraphService graphService;
2222
private readonly IOrganizationRepository organizationRepository;
2323
private readonly IProjectRepository projectRepository;
2424

2525
#pragma warning disable CS0618 // Type or member is obsolete
2626

27-
protected AdapterWithIdentity(IAuthorizationSessionClient sessionClient,
28-
IAuthorizationTokenClient tokenClient,
29-
IDistributedLockManager distributedLockManager,
30-
IAzureService azure,
31-
IGraphService graphService,
32-
IOrganizationRepository organizationRepository,
33-
IDeploymentScopeRepository deploymentScopeRepository,
34-
IProjectRepository projectRepository,
35-
IUserRepository userRepository) : base(sessionClient, tokenClient, distributedLockManager, azure, graphService, organizationRepository, deploymentScopeRepository, projectRepository, userRepository)
27+
protected AdapterWithIdentity(
28+
IAdapterProvider adapterProvider,
29+
IAuthorizationSessionClient sessionClient,
30+
IAuthorizationTokenClient tokenClient,
31+
IDistributedLockManager distributedLockManager,
32+
IAzureService azureService,
33+
IGraphService graphService,
34+
IOrganizationRepository organizationRepository,
35+
IDeploymentScopeRepository deploymentScopeRepository,
36+
IProjectRepository projectRepository,
37+
IUserRepository userRepository)
38+
: base(
39+
adapterProvider,
40+
sessionClient,
41+
tokenClient,
42+
distributedLockManager,
43+
azureService,
44+
graphService,
45+
organizationRepository,
46+
deploymentScopeRepository,
47+
projectRepository,
48+
userRepository)
3649
{
37-
this.azure = azure ?? throw new ArgumentNullException(nameof(azure));
50+
this.azureService = azureService ?? throw new ArgumentNullException(nameof(azureService));
3851
this.graphService = graphService ?? throw new ArgumentNullException(nameof(graphService));
3952
this.organizationRepository = organizationRepository ?? throw new ArgumentNullException(nameof(organizationRepository));
4053
this.projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository));
@@ -65,7 +78,7 @@ public virtual async Task<AzureServicePrincipal> GetServiceIdentityAsync(Compone
6578
.CreateServicePrincipalAsync(servicePrincipalName)
6679
.ConfigureAwait(false);
6780
}
68-
else if (servicePrincipal.ExpiresOn.GetValueOrDefault(DateTime.MinValue) < DateTime.UtcNow)
81+
else if (servicePrincipal.ExpiresOn.GetValueOrDefault(DateTime.MinValue.ToUniversalTime()) < DateTime.UtcNow)
6982
{
7083
// a service principal exists, but its secret is expired. lets refresh
7184
// the service principal (create a new secret) so we can move on
@@ -82,7 +95,7 @@ public virtual async Task<AzureServicePrincipal> GetServiceIdentityAsync(Compone
8295
.GetAsync(component.Organization, component.ProjectId)
8396
.ConfigureAwait(false);
8497

85-
var secretClient = await azure.KeyVaults
98+
var secretClient = await azureService.KeyVaults
8699
.GetSecretClientAsync(project.SecretsVaultId, ensureIdentityAccess: true)
87100
.ConfigureAwait(false);
88101

@@ -96,7 +109,7 @@ public virtual async Task<AzureServicePrincipal> GetServiceIdentityAsync(Compone
96109
.GetAsync(component.Organization, component.ProjectId)
97110
.ConfigureAwait(false);
98111

99-
var secretClient = await azure.KeyVaults
112+
var secretClient = await azureService.KeyVaults
100113
.GetSecretClientAsync(project.SecretsVaultId, ensureIdentityAccess: true)
101114
.ConfigureAwait(false);
102115

@@ -134,7 +147,7 @@ public virtual async Task<AzureServicePrincipal> GetServiceIdentityAsync(Deploym
134147
.CreateServicePrincipalAsync(servicePrincipalName)
135148
.ConfigureAwait(false);
136149
}
137-
else if (servicePrincipal.ExpiresOn.GetValueOrDefault(DateTime.MinValue) < DateTime.UtcNow)
150+
else if (servicePrincipal.ExpiresOn.GetValueOrDefault(DateTime.MinValue.ToUniversalTime()) < DateTime.UtcNow)
138151
{
139152
// a service principal exists, but its secret is expired. lets refresh
140153
// the service principal (create a new secret) so we can move on
@@ -147,15 +160,15 @@ public virtual async Task<AzureServicePrincipal> GetServiceIdentityAsync(Deploym
147160

148161
if (!string.IsNullOrEmpty(servicePrincipal.Password))
149162
{
150-
var tenantId = await azure
163+
var tenantId = await azureService
151164
.GetTenantIdAsync()
152165
.ConfigureAwait(false);
153166

154167
var organization = await organizationRepository
155168
.GetAsync(tenantId, deploymentScope.Organization)
156169
.ConfigureAwait(false);
157170

158-
var secretClient = await azure.KeyVaults
171+
var secretClient = await azureService.KeyVaults
159172
.GetSecretClientAsync(organization.SecretsVaultId, ensureIdentityAccess: true)
160173
.ConfigureAwait(false);
161174

@@ -165,15 +178,15 @@ public virtual async Task<AzureServicePrincipal> GetServiceIdentityAsync(Deploym
165178
}
166179
else if (withPassword)
167180
{
168-
var tenantId = await azure
181+
var tenantId = await azureService
169182
.GetTenantIdAsync()
170183
.ConfigureAwait(false);
171184

172185
var organization = await organizationRepository
173186
.GetAsync(tenantId, deploymentScope.Organization)
174187
.ConfigureAwait(false);
175188

176-
var secretClient = await azure.KeyVaults
189+
var secretClient = await azureService.KeyVaults
177190
.GetSecretClientAsync(organization.SecretsVaultId, ensureIdentityAccess: true)
178191
.ConfigureAwait(false);
179192

0 commit comments

Comments
 (0)