-
Notifications
You must be signed in to change notification settings - Fork 21
Feature/modernizing #30
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
Changes from all commits
ab27fe3
998764e
8abe284
9e69d11
b1575c2
4f17c10
5571d91
74a693b
4ea7d70
415a604
cfe693b
969df9f
e5a58b4
ded5cdf
efaa48b
2a22290
dfe2eb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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(); | ||
| } | ||
|
Comment on lines
+26
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optimize the parsing logic in Consider using a more efficient parsing approach, such as regular expressions, to handle complex header formats and improve readability. // Example: Consider using regular expressions for parsing. |
||
|
|
||
| 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; | ||
| } | ||
|
Comment on lines
+84
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure robust error handling in The methods throw // Example: Log malformed input for diagnostics.
if (equalPos < 1)
{
// Log the malformed pair for debugging purposes.
throw new FormatException("No '=' found.");
} |
||
| } | ||
| 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; | ||
| } | ||
|
Comment on lines
+49
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle potential null values gracefully. The if (header == null)
{
// Log the missing header scenario
throw new InvalidOperationException(
$"No WWW-Authenticate challenge was found for schema {schema}. Response: {response}");
} |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid initializing OAuthClient directly.
Consider using dependency injection to initialize
_client. This approach enhances testability and flexibility.Committable suggestion