mirror of
https://github.com/actions/runner.git
synced 2025-12-14 22:24:11 +00:00
GitHub Actions Runner
This commit is contained in:
172
src/Sdk/AadAuthentication/VssFederatedCredential.cs
Normal file
172
src/Sdk/AadAuthentication/VssFederatedCredential.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.Common.Internal;
|
||||
|
||||
namespace GitHub.Services.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides federated authentication with a hosted <c>VssConnection</c> instance using cookies.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class VssFederatedCredential : FederatedCredential
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <c>VssFederatedCredential</c> instance.
|
||||
/// </summary>
|
||||
public VssFederatedCredential()
|
||||
: this(true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <c>VssFederatedCredential</c> instance.
|
||||
/// </summary>
|
||||
public VssFederatedCredential(Boolean useCache)
|
||||
: this(useCache, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <c>VssFederatedCredential</c> instance.
|
||||
/// </summary>
|
||||
/// <param name="initialToken">The initial token if available</param>
|
||||
public VssFederatedCredential(VssFederatedToken initialToken)
|
||||
: this(false, initialToken)
|
||||
{
|
||||
}
|
||||
|
||||
public VssFederatedCredential(
|
||||
Boolean useCache,
|
||||
VssFederatedToken initialToken)
|
||||
: base(initialToken)
|
||||
{
|
||||
#if !NETSTANDARD
|
||||
if (useCache)
|
||||
{
|
||||
Storage = new VssClientCredentialStorage();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public override VssCredentialsType CredentialType
|
||||
{
|
||||
get
|
||||
{
|
||||
return VssCredentialsType.Federated;
|
||||
}
|
||||
}
|
||||
|
||||
public override Boolean IsAuthenticationChallenge(IHttpResponse webResponse)
|
||||
{
|
||||
bool isNonAuthenticationChallenge = false;
|
||||
return IsVssFederatedAuthenticationChallenge(webResponse, out isNonAuthenticationChallenge) ?? isNonAuthenticationChallenge;
|
||||
}
|
||||
|
||||
protected override IssuedTokenProvider OnCreateTokenProvider(
|
||||
Uri serverUrl,
|
||||
IHttpResponse response)
|
||||
{
|
||||
// The response is only null when attempting to determine the most appropriate token provider to
|
||||
// use for the connection. The only way we should do anything here is if we have an initial token
|
||||
// since that means we can present something without making a server call.
|
||||
if (response == null && base.InitialToken == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Uri signInUrl = null;
|
||||
String realm = String.Empty;
|
||||
String issuer = String.Empty;
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
var location = response.Headers.GetValues(HttpHeaders.Location).FirstOrDefault();
|
||||
if (location == null)
|
||||
{
|
||||
location = response.Headers.GetValues(HttpHeaders.TfsFedAuthRedirect).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(location))
|
||||
{
|
||||
signInUrl = new Uri(location);
|
||||
}
|
||||
|
||||
// Inform the server that we support the javascript notify "smart client" pattern for ACS auth
|
||||
AddParameter(ref signInUrl, "protocol", "javascriptnotify");
|
||||
|
||||
// Do not automatically sign in with existing FedAuth cookie
|
||||
AddParameter(ref signInUrl, "force", "1");
|
||||
|
||||
GetRealmAndIssuer(response, out realm, out issuer);
|
||||
}
|
||||
|
||||
return new VssFederatedTokenProvider(this, serverUrl, signInUrl, issuer, realm);
|
||||
}
|
||||
|
||||
internal static void GetRealmAndIssuer(
|
||||
IHttpResponse response,
|
||||
out String realm,
|
||||
out String issuer)
|
||||
{
|
||||
realm = response.Headers.GetValues(HttpHeaders.TfsFedAuthRealm).FirstOrDefault();
|
||||
issuer = response.Headers.GetValues(HttpHeaders.TfsFedAuthIssuer).FirstOrDefault();
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(issuer))
|
||||
{
|
||||
issuer = new Uri(issuer).GetLeftPart(UriPartial.Authority);
|
||||
}
|
||||
}
|
||||
|
||||
internal static Boolean? IsVssFederatedAuthenticationChallenge(
|
||||
IHttpResponse webResponse,
|
||||
out Boolean isNonAuthenticationChallenge)
|
||||
{
|
||||
isNonAuthenticationChallenge = false;
|
||||
|
||||
if (webResponse == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to make sure that the redirect was issued from the Tfs service. We include the TfsServiceError
|
||||
// header to avoid the possibility that a redirect from a non-tfs service is issued and we incorrectly
|
||||
// launch the credentials UI.
|
||||
if (webResponse.StatusCode == HttpStatusCode.Found ||
|
||||
webResponse.StatusCode == HttpStatusCode.Redirect)
|
||||
{
|
||||
return webResponse.Headers.GetValues(HttpHeaders.Location).Any() && webResponse.Headers.GetValues(HttpHeaders.TfsFedAuthRealm).Any();
|
||||
}
|
||||
else if (webResponse.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
return webResponse.Headers.GetValues(HttpHeaders.WwwAuthenticate).Any(x => x.StartsWith("TFS-Federated", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else if (webResponse.StatusCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
// This is not strictly an "authentication challenge" but it is a state the user can do something about so they can get access to the resource
|
||||
// they are attempting to access. Specifically, the user will hit this when they need to update or create a profile required by business policy.
|
||||
isNonAuthenticationChallenge = webResponse.Headers.GetValues(HttpHeaders.TfsFedAuthRedirect).Any();
|
||||
if (isNonAuthenticationChallenge)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void AddParameter(ref Uri uri, String name, String value)
|
||||
{
|
||||
if (uri.Query.IndexOf(String.Concat(name, "="), StringComparison.OrdinalIgnoreCase) < 0)
|
||||
{
|
||||
UriBuilder builder = new UriBuilder(uri);
|
||||
builder.Query = String.Concat(builder.Query.TrimStart('?'), "&", name, "=", value);
|
||||
uri = builder.Uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user