Skip to content

Commit

Permalink
Merge pull request #30 from ChangemakerStudios/feature/Modernizing
Browse files Browse the repository at this point in the history
Merging this monkey... wish me luck.
  • Loading branch information
Jaben authored Aug 12, 2024
2 parents ee50d77 + dfe2eb6 commit 4882303
Show file tree
Hide file tree
Showing 140 changed files with 4,885 additions and 3,937 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@ on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [8.x]

steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Dotnet
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.100
dotnet-version: ${{ matrix.dotnet-version }}

- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0.9.7
uses: gittools/actions/gitversion/setup@v1.1.1
with:
versionSpec: '5.x'
versionSpec: '5.x'

- name: GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v0.9.7
uses: gittools/actions/gitversion/execute@v1.1.1
with:
useConfigFile: true

Expand All @@ -34,4 +37,5 @@ jobs:
- name: Publish
if: github.event_name != 'pull_request' && (github.ref_name == 'master')
run: |
dotnet nuget push **/*.nupkg --source 'https://api.nuget.org/v3/index.json' -k ${{ secrets.NUGETKEY }}
dotnet nuget push **/*.nupkg --source 'https://api.nuget.org/v3/index.json' -k ${{ secrets.NUGETKEY }} --skip-duplicate
dotnet nuget push **/*.snupkg --source 'https://api.nuget.org/v3/index.json' -k ${{ secrets.NUGETKEY }} --skip-duplicate
3 changes: 1 addition & 2 deletions Docker.Registry.DotNet.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32414.318
Expand All @@ -19,7 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0 - Assets", "0 - Assets",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3 - Samples", "3 - Samples", "{BBCE947D-B371-4738-98A2-F3FA9E934BF6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DockerRegistryExplorer", "samples\DockerRegistryExplorer\DockerRegistryExplorer.csproj", "{513C7B92-BFAF-4E0A-B8D9-FC0E7283CD62}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DockerRegistryExplorer", "samples\DockerRegistryExplorer\DockerRegistryExplorer.csproj", "{513C7B92-BFAF-4E0A-B8D9-FC0E7283CD62}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docker.Registry.Cli", "samples\Docker.Registry.Cli\Docker.Registry.Cli.csproj", "{DE73EA84-AE2A-4060-AA59-0EE409845232}"
EndProject
Expand Down
3 changes: 2 additions & 1 deletion Docker.Registry.DotNet.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue"> Copyright 2017-$CURRENT_YEAR$ Rich Quackenbush, Jaben Cargman&#xD;
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue"> Copyright 2017-${CurrentDate.Year} Rich Quackenbush, Jaben Cargman&#xD;
and Docker.Registry.DotNet Contributors&#xD;
&#xD;
Licensed under the Apache License, Version 2.0 (the "License");&#xD;
Expand All @@ -13,6 +13,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#xD;
See the License for the specific language governing permissions and&#xD;
limitations under the License.</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Cargman/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jaben/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Quackenbush/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
45 changes: 38 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,49 @@ dotnet add package Docker.Registry.DotNet
```

# Usage

### Local Hub

```csharp
var configuration = new RegistryClientConfiguration("localhost:5000");
var configuration = new RegistryClientConfiguration("http://localhost:5000");

//configuration.UsePasswordOAuthAuthentication("username", "password")
using (var client = configuration.CreateClient())
{
await client.System.PingAsync();
// get catalog
var catalog = await client.Catalog.GetCatalog();

// list tags for the first catalog
var tags = await client.Tags.ListTags(catalog?.Repositories.FirstOrDefault());
}
```

# Changelog
### Remote Hub with Authentication

```csharp
var configuration = new RegistryClientConfiguration("https://proget.mycompany.com");

configuration.UsePasswordOAuthAuthentication("username", "password")

using (var client = configuration.CreateClient())
{
// get catalog
var catalog = await client.Catalog.GetCatalog();

### v1.1.33
* Added Basic Authentication (thanks [Zguy](https://github.com/Zguy)).
* Fixed issue with operational parameters (thanks [lostllama](https://github.com/lostllama)).
* Fixed issue with large manifest layers (thanks [msvprogs](https://github.com/msvprogs)).
// list tags for the first catalog
var tags = await client.Tags.ListTags(catalog?.Repositories.FirstOrDefault());
}
```

### Docker Hub

```csharp
var configuration = new RegistryClientConfiguration("https://hub.docker.com");

using (var client = configuration.CreateClient())
{
// load respository
var tags = await client.Repository.ListRepositoryTags("grafana", "loki-docker-driver");
}
```
2 changes: 1 addition & 1 deletion samples/Docker.Registry.Cli/Docker.Registry.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
4 changes: 1 addition & 3 deletions samples/Docker.Registry.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
using System.Security.Cryptography;
using System.Threading.Tasks;
using Docker.Registry.DotNet;
using Docker.Registry.DotNet.Authentication;
using Docker.Registry.DotNet.Models;
using Docker.Registry.DotNet.Registry;
using Docker.Registry.DotNet.Domain.Registry;

namespace Docker.Registry.Cli
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ public void Refresh()

private async Task ListImagesTags()
{
var tags = await this._registryClient.Tags.ListImageTagsAsync(
var tags = await this._registryClient.Tags.ListTags(
this.Name,
new ListImageTagsParameters());
new ListTagsParameters());

if (tags.Tags == null) this.Tags = new TagViewModel[] { };
else
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2017-2022 Rich Quackenbush, Jaben Cargman
// and Docker.Registry.DotNet Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Docker.Registry.DotNet.Application.OAuth;

namespace Docker.Registry.DotNet.Application.Authentication;

[PublicAPI]
public class AnonymousOAuthAuthenticationProvider : AuthenticationProvider
{
private readonly OAuthClient _client = new();

private static string Schema { get; } = "Bearer";

public override Task Authenticate(HttpRequestMessage request, IRegistryUriBuilder uriBuilder)
{
using var activity = Assembly.Source.StartActivity("AnonymousOAuthAuthenticationProvider.Authenticate(request)");

return Task.CompletedTask;
}

public override async Task Authenticate(
HttpRequestMessage request,
HttpResponseMessage response,
IRegistryUriBuilder uriBuilder)
{
using var activity = Assembly.Source.StartActivity("AnonymousOAuthAuthenticationProvider.Authenticate(request, response)");

var header = this.TryGetSchemaHeader(response, Schema);

//Get the bearer bits
var bearerBits = AuthenticateParser.ParseTyped(header.Parameter);

//Get the token
var token = await this._client.GetToken(
bearerBits.Realm,
bearerBits.Service,
bearerBits.Scope);

//Set the header
request.Headers.Authorization = new AuthenticationHeaderValue(Schema, token.Token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright 2017-2022 Rich Quackenbush, Jaben Cargman
// and Docker.Registry.DotNet Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Docker.Registry.DotNet.Application.Authentication;

internal static class AuthenticateParser
{
public static IDictionary<string, string> Parse(string value)
{
//https://stackoverflow.com/questions/45516717/extracting-and-parsing-the-www-authenticate-header-from-httpresponsemessage-in/45516809#45516809
return SplitWWWAuthenticateHeader(value).ToDictionary(GetKey, GetValue);
}

private static IEnumerable<string> SplitWWWAuthenticateHeader(string value)
{
var builder = new StringBuilder();
var inQuotes = false;
for (var i = 0; i < value.Length; i++)
{
var charI = value[i];
switch (charI)
{
case '\"':
if (inQuotes)
{
yield return builder.ToString();
builder.Clear();
inQuotes = false;
}
else
{
inQuotes = true;
}

break;

case ',':
if (inQuotes)
{
builder.Append(charI);
}
else
{
if (builder.Length > 0)
{
yield return builder.ToString();
builder.Clear();
}
}

break;

default:
builder.Append(charI);
break;
}
}

if (builder.Length > 0) yield return builder.ToString();
}

public static ParsedAuthentication ParseTyped(string value)
{
var parsed = Parse(value);

return new ParsedAuthentication(
parsed.GetValueOrDefault("realm"),
parsed.GetValueOrDefault("service"),
parsed.GetValueOrDefault("scope"));
}

private static string GetKey(string pair)
{
var equalPos = pair.IndexOf("=", StringComparison.Ordinal);

if (equalPos < 1)
throw new FormatException("No '=' found.");

return pair.Substring(0, equalPos);
}

private static string GetValue(string pair)
{
var equalPos = pair.IndexOf("=", StringComparison.Ordinal);

if (equalPos < 1)
throw new FormatException("No '=' found.");

var value = pair.Substring(equalPos + 1).Trim();

//Trim quotes
if (value.StartsWith("\"") && value.EndsWith("\""))
value = value.Substring(1, value.Length - 2);

return value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2017-2022 Rich Quackenbush, Jaben Cargman
// and Docker.Registry.DotNet Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Docker.Registry.DotNet.Application.Authentication;

/// <summary>
/// Authentication provider.
/// </summary>
public abstract class AuthenticationProvider
{
/// <summary>
/// Called on initial connection
/// </summary>
/// <param name="request"></param>
/// <param name="uriBuilder"></param>
/// <returns></returns>
public abstract Task Authenticate(HttpRequestMessage request, IRegistryUriBuilder uriBuilder);

/// <summary>
/// Called when connection is challenged.
/// </summary>
/// <param name="request"></param>
/// <param name="response"></param>
/// <param name="builder"></param>
/// <returns></returns>
public abstract Task Authenticate(
HttpRequestMessage request,
HttpResponseMessage response,
IRegistryUriBuilder builder);

/// <summary>
/// Gets the schema header from the http response.
/// </summary>
/// <param name="response"></param>
/// <param name="schema"></param>
/// <returns></returns>
protected AuthenticationHeaderValue TryGetSchemaHeader(
HttpResponseMessage response,
string schema)
{
var header = response.GetHeaderBySchema(schema);

if (header == null)
throw new InvalidOperationException(
$"No WWW-Authenticate challenge was found for schema {schema}");

return header;
}
}
Loading

0 comments on commit 4882303

Please sign in to comment.