Skip to content

Commit

Permalink
[Rollout] 2024-06-13 (#3624)
Browse files Browse the repository at this point in the history
  • Loading branch information
dkurepa authored Jun 12, 2024
2 parents cce4665 + 6c96fe8 commit edcb1a8
Show file tree
Hide file tree
Showing 23 changed files with 263 additions and 93 deletions.
24 changes: 12 additions & 12 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,29 @@
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="8.0.0-beta.24270.4">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="8.0.0-beta.24310.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f2b2071632d5d4c46d0f904f2b0d917b1752551b</Sha>
<Sha>9f6799fdc16ae19b3e9478c55b997a6aab839d09</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.SignTool" Version="8.0.0-beta.24270.4">
<Dependency Name="Microsoft.DotNet.SignTool" Version="8.0.0-beta.24310.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f2b2071632d5d4c46d0f904f2b0d917b1752551b</Sha>
<Sha>9f6799fdc16ae19b3e9478c55b997a6aab839d09</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="8.0.0-beta.24270.4">
<Dependency Name="Microsoft.DotNet.Build.Tasks.Feed" Version="8.0.0-beta.24310.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f2b2071632d5d4c46d0f904f2b0d917b1752551b</Sha>
<Sha>9f6799fdc16ae19b3e9478c55b997a6aab839d09</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.SwaggerGenerator.MSBuild" Version="8.0.0-beta.24270.4">
<Dependency Name="Microsoft.DotNet.SwaggerGenerator.MSBuild" Version="8.0.0-beta.24310.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f2b2071632d5d4c46d0f904f2b0d917b1752551b</Sha>
<Sha>9f6799fdc16ae19b3e9478c55b997a6aab839d09</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Git.IssueManager" Version="8.0.0-beta.24270.4">
<Dependency Name="Microsoft.DotNet.Git.IssueManager" Version="8.0.0-beta.24310.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f2b2071632d5d4c46d0f904f2b0d917b1752551b</Sha>
<Sha>9f6799fdc16ae19b3e9478c55b997a6aab839d09</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.VersionTools" Version="8.0.0-beta.24270.4">
<Dependency Name="Microsoft.DotNet.VersionTools" Version="8.0.0-beta.24310.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>f2b2071632d5d4c46d0f904f2b0d917b1752551b</Sha>
<Sha>9f6799fdc16ae19b3e9478c55b997a6aab839d09</Sha>
</Dependency>
<Dependency Name="Microsoft.DncEng.SecretManager" Version="1.1.0-beta.24280.1">
<Uri>https://github.com/dotnet/dnceng</Uri>
Expand Down
10 changes: 5 additions & 5 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
<UsingToolNetFrameworkReferenceAssemblies>true</UsingToolNetFrameworkReferenceAssemblies>
<MicrosoftNetFrameworkReferenceAssembliesVersion>1.0.0-preview.1</MicrosoftNetFrameworkReferenceAssembliesVersion>
<!-- Libs -->
<MicrosoftDotNetSignToolVersion>8.0.0-beta.24270.4</MicrosoftDotNetSignToolVersion>
<MicrosoftDotNetBuildTasksFeedVersion>8.0.0-beta.24270.4</MicrosoftDotNetBuildTasksFeedVersion>
<MicrosoftDotNetSwaggerGeneratorMSBuildVersion>8.0.0-beta.24270.4</MicrosoftDotNetSwaggerGeneratorMSBuildVersion>
<MicrosoftDotNetGitIssueManagerVersion>8.0.0-beta.24270.4</MicrosoftDotNetGitIssueManagerVersion>
<MicrosoftDotNetVersionToolsVersion>8.0.0-beta.24270.4</MicrosoftDotNetVersionToolsVersion>
<MicrosoftDotNetSignToolVersion>8.0.0-beta.24310.5</MicrosoftDotNetSignToolVersion>
<MicrosoftDotNetBuildTasksFeedVersion>8.0.0-beta.24310.5</MicrosoftDotNetBuildTasksFeedVersion>
<MicrosoftDotNetSwaggerGeneratorMSBuildVersion>8.0.0-beta.24310.5</MicrosoftDotNetSwaggerGeneratorMSBuildVersion>
<MicrosoftDotNetGitIssueManagerVersion>8.0.0-beta.24310.5</MicrosoftDotNetGitIssueManagerVersion>
<MicrosoftDotNetVersionToolsVersion>8.0.0-beta.24310.5</MicrosoftDotNetVersionToolsVersion>
<MicrosoftNetTestSdkVersion>17.4.1</MicrosoftNetTestSdkVersion>
<MicrosoftDotNetInternalLoggingVersion>1.1.0-beta.24303.1</MicrosoftDotNetInternalLoggingVersion>
<MicrosoftAspNetCoreApiPaginationVersion>1.1.0-beta.24303.1</MicrosoftAspNetCoreApiPaginationVersion>
Expand Down
16 changes: 7 additions & 9 deletions eng/common/templates-official/job/source-index-stage1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
value: ${{ parameters.sourceIndexPackageSource }}
- name: BinlogPath
value: ${{ parameters.binlogPath }}
- template: /eng/common/templates/variables/pool-providers.yml
- template: /eng/common/templates-official/variables/pool-providers.yml

${{ if ne(parameters.pool, '') }}:
pool: ${{ parameters.pool }}
Expand All @@ -34,7 +34,8 @@ jobs:
demands: ImageOverride -equals windows.vs2019.amd64.open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
name: $(DncEngInternalBuildPool)
demands: ImageOverride -equals windows.vs2019.amd64
image: windows.vs2022.amd64
os: windows

steps:
- ${{ each preStep in parameters.preSteps }}:
Expand Down Expand Up @@ -70,16 +71,13 @@ jobs:
scriptType: 'ps'
scriptLocation: 'inlineScript'
inlineScript: |
echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId"
echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken"
echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId"
echo "##vso[task.setvariable variable=ARM_CLIENT_ID;issecret=true]$env:servicePrincipalId"
echo "##vso[task.setvariable variable=ARM_ID_TOKEN;issecret=true]$env:idToken"
echo "##vso[task.setvariable variable=ARM_TENANT_ID;issecret=true]$env:tenantId"
- script: |
echo "Client ID: $(ARM_CLIENT_ID)"
echo "ID Token: $(ARM_ID_TOKEN)"
echo "Tenant ID: $(ARM_TENANT_ID)"
az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN)
displayName: "Login to Azure"
- script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1
displayName: Upload stage1 artifacts to source index
displayName: Upload stage1 artifacts to source index
11 changes: 4 additions & 7 deletions eng/common/templates/job/source-index-stage1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,13 @@ jobs:
scriptType: 'ps'
scriptLocation: 'inlineScript'
inlineScript: |
echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId"
echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken"
echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId"
echo "##vso[task.setvariable variable=ARM_CLIENT_ID;issecret=true]$env:servicePrincipalId"
echo "##vso[task.setvariable variable=ARM_ID_TOKEN;issecret=true]$env:idToken"
echo "##vso[task.setvariable variable=ARM_TENANT_ID;issecret=true]$env:tenantId"
- script: |
echo "Client ID: $(ARM_CLIENT_ID)"
echo "ID Token: $(ARM_ID_TOKEN)"
echo "Tenant ID: $(ARM_TENANT_ID)"
az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN)
displayName: "Login to Azure"
- script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1
displayName: Upload stage1 artifacts to source index
displayName: Upload stage1 artifacts to source index
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
}
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24270.4"
"Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24310.5"
}
}
18 changes: 13 additions & 5 deletions src/Maestro/Client/src/MaestroApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,34 +68,42 @@ partial void HandleFailedRequest(RestApiException ex)
/// <param name="disableInteractiveAuth">Whether to include interactive login flows</param>
/// <param name="barApiToken">Token to use for the call. If none supplied, will try other flows.</param>
/// <param name="federatedToken">Federated token to use for the call. If none supplied, will try other flows.</param>
/// <param name="managedIdentityId">Managed Identity to use for the auth</param>
/// <returns>Credential that can be used to call the Maestro API</returns>
public static TokenCredential CreateApiCredential(
string barApiBaseUri,
bool disableInteractiveAuth,
string? barApiToken = null,
string? federatedToken = null)
string? federatedToken = null,
string? managedIdentityId = null)
{
// 1. BAR or Entra token that can directly be used to authenticate against Maestro
if (!string.IsNullOrEmpty(barApiToken))
{
return new MaestroApiTokenCredential(barApiToken!);
}

// 2. Federated token that can be used to fetch an app token (CI scenario)
barApiBaseUri ??= ProductionBuildAssetRegistryBaseUri;

// 2. Federated token that can be used to fetch an app token (for CI scenarios)
if (!string.IsNullOrEmpty(federatedToken))
{
return MaestroApiCredential.CreateFederatedCredential(barApiBaseUri, federatedToken!);
}

barApiBaseUri ??= ProductionBuildAssetRegistryBaseUri;
// 3. Managed identity (for server-to-server scenarios - e.g. PCS->Maestro)
if (!string.IsNullOrEmpty(managedIdentityId))
{
return MaestroApiCredential.CreateManagedIdentityCredential(barApiBaseUri, managedIdentityId!);
}

// 3. Azure CLI authentication setup by the caller (CI scenario)
// 4. Azure CLI authentication setup by the caller (for CI scenarios)
if (disableInteractiveAuth)
{
return MaestroApiCredential.CreateNonUserCredential(barApiBaseUri);
}

// 4. Interactive login (user-based scenario)
// 5. Interactive login (user-based scenario)
return MaestroApiCredential.CreateUserCredential(barApiBaseUri);
}
}
Expand Down
82 changes: 77 additions & 5 deletions src/Maestro/Client/src/MaestroApiCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
Expand All @@ -19,6 +20,8 @@ internal class MaestroApiCredential : TokenCredential
private const string TENANT_ID = "72f988bf-86f1-41af-91ab-2d7cd011db47";
private const string USER_SCOPE = "Maestro.User";

private const string AUTH_RECORD_PREFIX = ".auth-record";

private static readonly Dictionary<string, string> EntraAppIds = new Dictionary<string, string>
{
[MaestroApi.StagingBuildAssetRegistryBaseUri.TrimEnd('/')] = "baf98f1b-374e-487d-af42-aa33807f11e4",
Expand Down Expand Up @@ -54,21 +57,72 @@ public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext _, Canc
internal static MaestroApiCredential CreateUserCredential(string barApiBaseUri)
{
string appId = EntraAppIds[barApiBaseUri.TrimEnd('/')];
var requestContext = new TokenRequestContext(new string[] { $"api://{appId}/{USER_SCOPE}" });

string authRecordPath = Path.Combine(MaestroApiOptions.AUTH_CACHE, $"{AUTH_RECORD_PREFIX}-{appId}");
var credential = GetInteractiveCredential(appId, requestContext, authRecordPath);

return new MaestroApiCredential(credential, requestContext);
}

// This is a usual credential obtained against an entra app through a browser sign-in
var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions
/// <summary>
/// Create interactive credential from an authentication record stored in local cache
/// Authentication record is a set of app and user-specific metadata used by the library to authenticate
/// </summary>
private static InteractiveBrowserCredential GetInteractiveCredential(
string appId,
TokenRequestContext requestContext,
string authRecordPath)
{
// This is a usual configuration for a credential obtained against an entra app through a browser sign-in
var credentialOptions = new InteractiveBrowserCredentialOptions
{
TenantId = TENANT_ID,
ClientId = appId,
RedirectUri = new Uri("http://localhost"),
TokenCachePersistenceOptions = new TokenCachePersistenceOptions()
{
Name = "darc"
},
};


string authRecordDir = Path.GetDirectoryName(authRecordPath) ??
throw new ArgumentException($"Cannot resolve cache dir from auth record: {authRecordPath}");

if (!Directory.Exists(authRecordDir))
{
Directory.CreateDirectory(authRecordDir);
}

if (File.Exists(authRecordPath))
{
try
{
// Fetch existing authentication record to not prompt the user for consent
using var authRecordReadStream = new FileStream(authRecordPath, FileMode.Open, FileAccess.Read);
credentialOptions.AuthenticationRecord = AuthenticationRecord.Deserialize(authRecordReadStream);
}
});
catch
{
// We failed to read the authentication record, we should delete the invalid file and re-create it
File.Delete(authRecordPath);

var requestContext = new TokenRequestContext(new string[] { $"api://{appId}/{USER_SCOPE}" });
return new MaestroApiCredential(credential, requestContext);
return GetInteractiveCredential(appId, requestContext, authRecordPath);
}

return new InteractiveBrowserCredential(credentialOptions);
}

var credential = new InteractiveBrowserCredential(credentialOptions);

// Prompt the user for consent and save the resulting authentication record on disk
var authRecord = credential.Authenticate(requestContext);

using var authRecordStream = new FileStream(authRecordPath, FileMode.Create, FileAccess.Write);
authRecord.Serialize(authRecordStream);

return credential;
}

/// <summary>
Expand All @@ -87,6 +141,24 @@ internal static MaestroApiCredential CreateFederatedCredential(string barApiBase
return new MaestroApiCredential(credential, requestContext);
}

/// <summary>
/// Use this for darc invocations from services using an MI
/// </summary>
internal static MaestroApiCredential CreateManagedIdentityCredential(string barApiBaseUri, string managedIdentityId)
{
string appId = EntraAppIds[barApiBaseUri.TrimEnd('/')];

var miCredential = new ManagedIdentityCredential(managedIdentityId);

var appCredential = new ClientAssertionCredential(
TENANT_ID,
appId,
async (ct) => (await miCredential.GetTokenAsync(new TokenRequestContext(new string[] { "api://AzureADTokenExchange" }), ct)).Token);

var requestContext = new TokenRequestContext(new string[] { $"api://{appId}/.default" });
return new MaestroApiCredential(appCredential, requestContext);
}

/// <summary>
/// Use this for darc invocations from pipelines without a token.
/// </summary>
Expand Down
29 changes: 25 additions & 4 deletions src/Maestro/Client/src/MaestroApiFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,42 @@ public static class MaestroApiFactory
/// <param name="baseUri">URI of the build asset registry service to use.</param>
/// <param name="accessToken">Optional BAR token. When provided, will be used as the primary auth method.</param>
/// <param name="federatedToken">Optional federated credentials token.</param>
/// <param name="managedIdentityId">Managed Identity to use for the auth</param>
/// <param name="disableInteractiveAuth">Whether to include interactive login flows</param>
public static IMaestroApi GetAuthenticated(string baseUri, string? accessToken, string? federatedToken, bool disableInteractiveAuth)
public static IMaestroApi GetAuthenticated(
string baseUri,
string? accessToken,
string? federatedToken,
string? managedIdentityId,
bool disableInteractiveAuth)
{
return new MaestroApi(new MaestroApiOptions(baseUri, accessToken, federatedToken, disableInteractiveAuth));
return new MaestroApi(new MaestroApiOptions(
baseUri,
accessToken,
managedIdentityId,
federatedToken,
disableInteractiveAuth));
}

/// <summary>
/// Obtains API client for authenticated access to Maestro.
/// </summary>
/// <param name="accessToken">Optional BAR token. When provided, will be used as the primary auth method.</param>
/// <param name="federatedToken">Optional federated token. When provided, will be used as the primary auth method.</param>
/// <param name="managedIdentityId">Managed Identity to use for the auth</param>
/// <param name="disableInteractiveAuth">Whether to include interactive login flows</param>
public static IMaestroApi GetAuthenticated(string? accessToken, string? federatedToken, bool disableInteractiveAuth)
public static IMaestroApi GetAuthenticated(
string? accessToken,
string? federatedToken,
string? managedIdentityId,
bool disableInteractiveAuth)
{
return new MaestroApi(new MaestroApiOptions(MaestroApi.StagingBuildAssetRegistryBaseUri, accessToken, federatedToken, disableInteractiveAuth));
return new MaestroApi(new MaestroApiOptions(
MaestroApi.StagingBuildAssetRegistryBaseUri,
accessToken,
managedIdentityId,
federatedToken,
disableInteractiveAuth));
}

/// <summary>
Expand Down
8 changes: 6 additions & 2 deletions src/Maestro/Client/src/MaestroApiOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using Azure.Core;
using Azure.Core.Pipeline;

Expand All @@ -10,17 +11,20 @@ namespace Microsoft.DotNet.Maestro.Client
{
public partial class MaestroApiOptions
{
public static readonly string AUTH_CACHE = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".darc");

/// <summary>
/// Creates a new instance of <see cref="MaestroApiOptions"/> with the provided base URI.
/// </summary>
/// <param name="baseUri">API base URI</param>
/// <param name="accessToken">Optional BAR token. When provided, will be used as the primary auth method.</param>
/// <param name="managedIdentityId">Managed Identity to use for the auth</param>
/// <param name="federatedToken">Optional federated token. When provided, will be used as the primary auth method.</param>
/// <param name="disableInteractiveAuth">Whether to include interactive login flows</param>
public MaestroApiOptions(string baseUri, string accessToken, string federatedToken, bool disableInteractiveAuth)
public MaestroApiOptions(string baseUri, string accessToken, string managedIdentityId, string federatedToken, bool disableInteractiveAuth)
: this(
new Uri(baseUri),
MaestroApi.CreateApiCredential(baseUri, disableInteractiveAuth, accessToken, federatedToken))
MaestroApi.CreateApiCredential(baseUri, disableInteractiveAuth, accessToken, federatedToken, managedIdentityId))
{
}

Expand Down
Loading

0 comments on commit edcb1a8

Please sign in to comment.