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

Private feed implementation #156

Merged
merged 35 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b29393e
Private feed implementation
tomasfil Jun 17, 2024
3967c2b
case insensitive username
tomasfil Jun 17, 2024
42908d3
Cleanup
tomasfil Jun 17, 2024
50e8448
Cleanup
tomasfil Jun 17, 2024
0c596a6
Implement multi auth
tomasfil Jun 18, 2024
0871aa2
Prevent null exception.
tomasfil Jun 18, 2024
2452fa2
Requested changes
tomasfil Jun 19, 2024
66d0016
fix comment
tomasfil Jun 19, 2024
0faf0e1
Documentation
tomasfil Jun 19, 2024
19ce37b
Update config docs
tomasfil Jun 19, 2024
b01e8e8
Update apikeys docs
tomasfil Jun 19, 2024
4c7c179
feedback impl
tomasfil Jun 27, 2024
9ff7a0a
Tests
tomasfil Jun 27, 2024
2685f4c
Integration tests for basic authentication
tomasfil Jun 27, 2024
b2e467e
CodeMaid
tomasfil Jun 27, 2024
8af4c64
Remove redundant call
tomasfil Jun 27, 2024
ddfac3e
Move auth handler and tests
tomasfil Jun 27, 2024
1463c75
ApiKey to object
tomasfil Jun 27, 2024
15319b2
Move auth config to extend BaGetterApplication
tomasfil Jun 27, 2024
13b9312
Merge branch 'main' into main
tomasfil Jun 27, 2024
31b2b22
Api keys move into auth
tomasfil Jun 28, 2024
1ba8428
docs
tomasfil Jun 28, 2024
351b491
formatting
tomasfil Jun 28, 2024
818c578
VS Package manager console guide
tomasfil Jul 1, 2024
6379f24
remove extra space
tomasfil Jul 1, 2024
c9fef19
Unify line style
tomasfil Jul 1, 2024
6e4bf54
Allow auth policies config
tomasfil Sep 5, 2024
d7eb09b
Using
tomasfil Sep 5, 2024
b1d06d9
Move auth config to Bagetter project
tomasfil Sep 5, 2024
cb65543
Remove sealed class
tomasfil Sep 5, 2024
901d731
Auth options comments
tomasfil Sep 5, 2024
075db6d
Fix tests
tomasfil Sep 5, 2024
6b63d79
Merge branch 'main' into main
seriouz Sep 7, 2024
59071b9
Fix build
tomasfil Oct 21, 2024
1a3b3ea
Merge branch 'main' into main
tomasfil Oct 21, 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
6 changes: 0 additions & 6 deletions src/BaGetter.Core/Configuration/AuthenticationOptions.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/BaGetter.Core/Configuration/BaGetterOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ public class BaGetterOptions

public StatisticsOptions Statistics { get; set; }

public AuthenticationOptions Authentication { get; set; }
public NugetAuthenticationOptions Authentication { get; set; }
}
6 changes: 6 additions & 0 deletions src/BaGetter.Core/Configuration/NugetAuthenticationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace BaGetter.Core;

public sealed class NugetAuthenticationOptions
{
public NugetCredentials[] Credentials { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace BaGetter.Core;
namespace BaGetter.Core;

public sealed class Credential
public sealed class NugetCredentials
{
public string Username { get; set; }

Expand Down
14 changes: 12 additions & 2 deletions src/BaGetter/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using BaGetter.Web;
using BaGetter.Web.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
Expand All @@ -30,8 +31,17 @@ public void ConfigureServices(IServiceCollection services)
services.ConfigureOptions<ValidateBaGetterOptions>();
services.ConfigureOptions<ConfigureBaGetterServer>();

services.AddAuthentication(AuthenticationConstants.NugetBasicAuthenticationScheme)
.AddScheme<AuthenticationSchemeOptions, NugetBasicAuthenticationHandler>(AuthenticationConstants.NugetBasicAuthenticationScheme, null);
services.AddAuthentication(options =>
{
// Breaks existing tests if the contains check is not here.
Regenhardt marked this conversation as resolved.
Show resolved Hide resolved
if (!options.SchemeMap.ContainsKey(AuthenticationConstants.NugetBasicAuthenticationScheme))
{
options.AddScheme<NugetBasicAuthenticationHandler>(AuthenticationConstants.NugetBasicAuthenticationScheme, AuthenticationConstants.NugetBasicAuthenticationScheme);
options.DefaultAuthenticateScheme = AuthenticationConstants.NugetBasicAuthenticationScheme;
options.DefaultChallengeScheme = AuthenticationConstants.NugetBasicAuthenticationScheme;
}
});

services.AddAuthorization(options =>
{
options.AddPolicy(AuthenticationConstants.NugetUserPolicy, policy =>
Expand Down
178 changes: 178 additions & 0 deletions tests/BaGetter.Tests/NugetBasicAuthenticationHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
using System;
Regenhardt marked this conversation as resolved.
Show resolved Hide resolved
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using BaGetter.Core;
using BaGetter.Web.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Moq;
using Xunit;

namespace BaGetter.Tests;

public class NugetBasicAuthenticationHandlerTests
{
private readonly Mock<IOptionsMonitor<AuthenticationSchemeOptions>> _options;
private readonly Mock<ILoggerFactory> _loggerFactory;
private readonly UrlEncoder _encoder;
private readonly Mock<IOptions<BaGetterOptions>> _bagetterOptions;
private readonly Mock<HttpContext> _httpContext;
private readonly Mock<HttpRequest> _httpRequest;
private readonly Mock<HttpResponse> _httpResponse;

public NugetBasicAuthenticationHandlerTests()
{
_options = new Mock<IOptionsMonitor<AuthenticationSchemeOptions>>();
_options.Setup(x => x.Get(It.IsAny<string>())).Returns(new AuthenticationSchemeOptions());

_loggerFactory = new Mock<ILoggerFactory>();
_loggerFactory.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(Mock.Of<ILogger<NugetBasicAuthenticationHandler>>());

_encoder = UrlEncoder.Default;

_bagetterOptions = new Mock<IOptions<BaGetterOptions>>();

_httpContext = new Mock<HttpContext>();
_httpRequest = new Mock<HttpRequest>();
_httpResponse = new Mock<HttpResponse>();

_httpContext.SetupGet(x => x.Request).Returns(_httpRequest.Object);
_httpContext.SetupGet(x => x.Response).Returns(_httpResponse.Object);
}

[Fact]
public async Task HandleAuthenticateAsync_AnonymousAllowed_ReturnsSuccessResult()
{
// Arrange
SetupBaGetterOptions(new BaGetterOptions());
var handler = CreateHandler();

// Act
var result = await handler.AuthenticateAsync();

// Assert
Assert.True(result.Succeeded);
Assert.True(result.Principal.HasClaim(ClaimTypes.Anonymous, string.Empty));
}

[Fact]
public async Task HandleAuthenticateAsync_NoAuthorizationHeader_ReturnsNoResult()
{
// Arrange
SetupBaGetterOptions(new BaGetterOptions
{
Authentication = new NugetAuthenticationOptions
{
Credentials = [new NugetCredentials { Username = "user", Password = "pass" }]
}
});
_httpRequest.Setup(r => r.Headers).Returns(new HeaderDictionary());
var handler = CreateHandler();

// Act
var result = await handler.AuthenticateAsync();

// Assert
Assert.True(result.None);
}

[Fact]
public async Task HandleAuthenticateAsync_InvalidAuthorizationHeader_ReturnsFailResult()
{
// Arrange
SetupBaGetterOptions(new BaGetterOptions
{
Authentication = new NugetAuthenticationOptions
{
Credentials = [new NugetCredentials { Username = "user", Password = "pass" }]
}
});
_httpRequest.Setup(r => r.Headers).Returns(new HeaderDictionary
{
{ "Authorization", new StringValues("InvalidHeader") }
});
var handler = CreateHandler();

// Act
var result = await handler.AuthenticateAsync();

// Assert
Assert.False(result.Succeeded);
Assert.Equal("Invalid Authorization Header", result.Failure.Message);
}

[Fact]
public async Task HandleAuthenticateAsync_ValidCredentials_ReturnsSuccessResult()
{
// Arrange
const string username = "testuser";
const string password = "testpass";
SetupBaGetterOptions(new BaGetterOptions
{
Authentication = new NugetAuthenticationOptions
{
Credentials = [new NugetCredentials { Username = username, Password = password }]
}
});
_httpRequest.Setup(r => r.Headers).Returns(new HeaderDictionary
{
{ "Authorization", new StringValues($"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"))}") }
});
var handler = CreateHandler();

// Act
var result = await handler.AuthenticateAsync();

// Assert
Assert.True(result.Succeeded);
Assert.True(result.Principal.HasClaim(ClaimTypes.Name, username));
}

[Fact]
public async Task HandleAuthenticateAsync_InvalidCredentials_ReturnsFailResult()
{
// Arrange
SetupBaGetterOptions(new BaGetterOptions
{
Authentication = new NugetAuthenticationOptions
{
Credentials = [new NugetCredentials { Username = "user", Password = "pass" }]
}
});
_httpRequest.Setup(r => r.Headers).Returns(new HeaderDictionary
{
{ "Authorization", new StringValues($"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes("invaliduser:invalidpass"))}") }
});
var handler = CreateHandler();

// Act
var result = await handler.AuthenticateAsync();

// Assert
Assert.False(result.Succeeded);
Assert.Equal("Invalid Username or Password", result.Failure.Message);
}

private void SetupBaGetterOptions(BaGetterOptions options)
{
_bagetterOptions.Setup(x => x.Value).Returns(options);
}

private NugetBasicAuthenticationHandler CreateHandler()
{
var handler = new NugetBasicAuthenticationHandler(
_options.Object,
_loggerFactory.Object,
_encoder,
_bagetterOptions.Object);

handler.InitializeAsync(new AuthenticationScheme("Basic", null, typeof(NugetBasicAuthenticationHandler)), _httpContext.Object).GetAwaiter().GetResult();

return handler;
}
}