Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support service principal #10043

Merged
merged 63 commits into from
Jul 2, 2024
Merged
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
018faec
WIP
agr May 21, 2024
56ec658
Unused args.
agr May 21, 2024
5dda8bf
Removing BlobRequestOptions
agr May 22, 2024
cf3c800
Less usings.
agr May 22, 2024
812662d
BlobContinuationTokens.
agr May 22, 2024
cb98c41
BLob list details wrapper.
agr May 22, 2024
ed59f66
BlobProperties
agr May 23, 2024
99b4180
CopyState
agr May 23, 2024
6d65715
AccessCondition
agr May 23, 2024
f81cc7f
SAS permissions enum
agr May 23, 2024
435d521
Method rename
agr May 23, 2024
217c7e6
ContentEncoding property
agr May 24, 2024
5526e3d
Overwrite fix.
agr May 24, 2024
379d154
Exceptions!
agr May 25, 2024
d08d4e6
Pulling back nuget.jobs changes.
agr May 28, 2024
cdf1063
WrapStorageExceptionAsync
agr May 29, 2024
bfd2449
Missed `?`
agr May 29, 2024
b919573
Catching more cases of blob/container not found.
agr May 29, 2024
c747941
Aligning exception checks with old code.
agr May 29, 2024
5b72650
NuGet.Jobs version update
agr May 29, 2024
97937be
WIP
agr May 31, 2024
20b1ca6
WIP
agr Jun 1, 2024
32428bc
WIP
agr Jun 5, 2024
617b1b4
WIP
agr Jun 5, 2024
e0f4793
WIP
agr Jun 5, 2024
1f68b56
WIP
agr Jun 6, 2024
471e8fe
WIP
agr Jun 6, 2024
e26c9bc
Compiles
agr Jun 6, 2024
f901d4a
Test "fixes"
agr Jun 6, 2024
8fdc168
Connection string fixes.
agr Jun 6, 2024
a35e574
More binding redirects.
agr Jun 6, 2024
76c954e
FetchAttributes after blob upload because calling code expects to be …
agr Jun 7, 2024
290ecc7
Using DownloadContent for DownloadText.
agr Jun 7, 2024
bba277f
Request timeout in CloudBlobClientWrapper constructor.
agr Jun 10, 2024
fc24eb3
FetchAttributes in OpenRead call as there is expectations that proper…
agr Jun 11, 2024
c6eab65
Better(?) way to get properties/metadata for OpenReadAsync.
agr Jun 11, 2024
fe65c21
The expectations are that we should always be able to set HTTP headers.
agr Jun 12, 2024
946c6de
More FetchAttributes
agr Jun 14, 2024
ce5112c
Test fix
agr Jun 14, 2024
d173bda
named parameter
agr Jun 14, 2024
b452051
Dropping Continuation token interface.
agr Jun 14, 2024
a0f87db
Preserving SAS URL for copying.
agr Jun 14, 2024
f88895d
Continuation token.
agr Jun 14, 2024
b3a543f
CloudBlobWrapper.Uri would not contain query
agr Jun 18, 2024
1af9756
PR feedback.
agr Jun 19, 2024
ae380dc
More PR feedback.
agr Jun 19, 2024
c6d0cfa
Less warnings.
agr Jun 20, 2024
e96561e
fix Priviledged typo
SimonCropp Jun 5, 2024
7c88c7e
Bringing back updated NuGet.Jobs packges.
agr Jun 21, 2024
e0174a0
Merge remote-tracking branch 'origin/dev' into agr-st-sdk
agr Jun 25, 2024
51e101b
Cosntructor argument type fix
agr Jun 25, 2024
8c6d504
Tests
agr Jun 25, 2024
a857b1d
Dropped blob client wrapper facts.
agr Jun 25, 2024
d9a1d9b
Not using HasFlag
agr Jun 25, 2024
eb0f21b
Comment update.
agr Jun 25, 2024
f4e9512
support service principal
Jun 26, 2024
8ca81a7
space
Jun 27, 2024
704bbb5
Merge branch 'dev' into arg-st-sdk-geneva-version
Jun 28, 2024
5db5e28
resolve conflict
Jun 28, 2024
dcd25b2
support sp
Jun 28, 2024
124503a
Merge branch 'dev' into arg-st-sdk-geneva-version
Jun 28, 2024
30ed904
update the sp auth
Jun 28, 2024
95c2aaf
s
Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/NuGetGallery.Core/Services/CloudBlobClientWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Azure;
using Azure.Core;
using Azure.Identity;
Expand Down Expand Up @@ -57,6 +59,19 @@ public static CloudBlobClientWrapper UsingMsi(
return new CloudBlobClientWrapper(storageConnectionString, tokenCredential, readAccessGeoRedundant, requestTimeout);
}

public static CloudBlobClientWrapper UsingServicePrincipal(
string storageConnectionString,
string appID,
string subjectAlternativeName,
string tenantId,
string authorityHost,
bool readAccessGeoRedundant = false,
TimeSpan? requestTimeout = null)
{
var tokenCredential = GetCredentialUsingServicePrincipal(appID, subjectAlternativeName, tenantId, authorityHost);
return new CloudBlobClientWrapper(storageConnectionString, tokenCredential, readAccessGeoRedundant, requestTimeout);
}

public ISimpleCloudBlob GetBlobFromUri(Uri uri)
{
// For Azure blobs, the query string is assumed to be the SAS token.
Expand Down Expand Up @@ -217,5 +232,36 @@ private string GetSecondaryConnectionString()
.Replace($"AccountName={primaryAccountName};", $"AccountName={secondaryAccountName};");
return secondaryConnectionString;
}

/// <summary>
/// Gets credential using the Service Principal. If the resource is in a different tenant, this is how to access it.
/// The ServicePrincipal needs to be a "Storage Table/Blob/Queue Data Contributor" role on the storage account. Owner isn't enough.
/// </summary>
/// <returns>ClientCertificatCredential to be used to communicate with Storage.</returns>
private static ClientCertificateCredential GetCredentialUsingServicePrincipal(string appID, string subjectAlternativeName, string tenantId, string authorityHost)
{
X509Certificate2 clientCert;

// Azure.Identity library doesn't support referencing cert by Store + Subject name, so we need to load it ourselves.
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
store.Open(OpenFlags.ReadOnly);

X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectName, subjectAlternativeName, true);

if (certs.Count == 0)
{
throw new InvalidOperationException($"Unable to find certificate with subject name '{subjectAlternativeName}'");
}

// As an exception to comment in GetKeyVaultCertsAsync method, this X509Certificate2 object does not have to be disposed
// because it is referencing a platform certificate from CurrentUser certificate store, so no temporary files are created for this object.
clientCert = certs.Cast<X509Certificate2>()
.Where(c => c.NotBefore < DateTime.UtcNow && c.NotAfter > DateTime.UtcNow)
.OrderBy(x => x.NotAfter).Last();
lyndaidaii marked this conversation as resolved.
Show resolved Hide resolved
}

return new ClientCertificateCredential(tenantId, appID, clientCert, new ClientCertificateCredentialOptions { AuthorityHost = new Uri(authorityHost), SendCertificateChain = true });
}
}
}