ce7cb702218693d08ea6d83464d2457c9250027c
This commit is contained in:
commit
34c5f8963c
1 changed files with 145336 additions and 0 deletions
120
packages/fsharp-runner/oci/RegistryAuth.cs
Normal file
120
packages/fsharp-runner/oci/RegistryAuth.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Quorate.Oci;
|
||||
|
||||
public sealed class RegistryAuth
|
||||
{
|
||||
internal RegistryAuth(string user, string password)
|
||||
{
|
||||
User = user;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
internal string User { get; }
|
||||
internal string Password { get; }
|
||||
|
||||
public static RegistryAuth Load(string path, string registry)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(path);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(registry);
|
||||
|
||||
try
|
||||
{
|
||||
using FileStream stream = File.OpenRead(path);
|
||||
DockerConfigModel config = JsonSerializer.Deserialize(stream, OciJsonContext.Default.DockerConfigModel)
|
||||
?? throw new InvalidDataException("Registry credential document is empty.");
|
||||
Dictionary<string, DockerAuthModel> auths = config.Auths
|
||||
?? throw new InvalidDataException("Registry credential document has no auths.");
|
||||
|
||||
if (!auths.TryGetValue(registry, out DockerAuthModel? entry) &&
|
||||
!auths.TryGetValue($"https://{registry}", out entry))
|
||||
{
|
||||
throw new InvalidDataException($"Registry credentials are unavailable for {registry}.");
|
||||
}
|
||||
|
||||
string encoded = entry.Auth
|
||||
?? throw new InvalidDataException("Registry credentials are invalid.");
|
||||
string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(encoded));
|
||||
int separator = decoded.IndexOf(':', StringComparison.Ordinal);
|
||||
|
||||
if (separator < 1)
|
||||
{
|
||||
throw new InvalidDataException("Registry credentials are invalid.");
|
||||
}
|
||||
|
||||
return new RegistryAuth(decoded[..separator], decoded[(separator + 1)..]);
|
||||
}
|
||||
catch (Exception error) when (error is JsonException or FormatException)
|
||||
{
|
||||
throw new InvalidDataException("Registry credentials are invalid.", error);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => nameof(RegistryAuth);
|
||||
|
||||
internal AuthenticationHeaderValue Basic() => new(
|
||||
"Basic",
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes($"{User}:{Password}")));
|
||||
}
|
||||
|
||||
internal sealed record BearerChallenge(Uri Realm, string? Service, string? Scope)
|
||||
{
|
||||
public static BearerChallenge Parse(AuthenticationHeaderValue value)
|
||||
{
|
||||
if (!value.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.IsNullOrWhiteSpace(value.Parameter))
|
||||
{
|
||||
throw new InvalidDataException("Registry bearer challenge is invalid.");
|
||||
}
|
||||
|
||||
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
int offset = 0;
|
||||
string parameter = value.Parameter;
|
||||
|
||||
while (offset < parameter.Length)
|
||||
{
|
||||
while (offset < parameter.Length && (parameter[offset] == ',' || char.IsWhiteSpace(parameter[offset])))
|
||||
{
|
||||
offset++;
|
||||
}
|
||||
|
||||
int equals = parameter.IndexOf('=', offset);
|
||||
|
||||
if (equals <= offset)
|
||||
{
|
||||
throw new InvalidDataException("Registry bearer challenge is invalid.");
|
||||
}
|
||||
|
||||
string name = parameter[offset..equals].Trim();
|
||||
offset = equals + 1;
|
||||
|
||||
if (offset >= parameter.Length || parameter[offset] != '"')
|
||||
{
|
||||
throw new InvalidDataException("Registry bearer challenge is invalid.");
|
||||
}
|
||||
|
||||
int end = parameter.IndexOf('"', ++offset);
|
||||
|
||||
if (end < 0)
|
||||
{
|
||||
throw new InvalidDataException("Registry bearer challenge is invalid.");
|
||||
}
|
||||
|
||||
values.Add(name, parameter[offset..end]);
|
||||
offset = end + 1;
|
||||
}
|
||||
|
||||
if (!values.TryGetValue("realm", out string? realmValue) ||
|
||||
!Uri.TryCreate(realmValue, UriKind.Absolute, out Uri? realm) ||
|
||||
realm.Scheme != Uri.UriSchemeHttps)
|
||||
{
|
||||
throw new InvalidDataException("Registry token endpoint must use HTTPS.");
|
||||
}
|
||||
|
||||
values.TryGetValue("service", out string? service);
|
||||
values.TryGetValue("scope", out string? scope);
|
||||
return new BearerChallenge(realm, service, scope);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue