Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions SaveHere/SaveHere.Tests/SaveHere.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.22" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.23" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.11" />
</ItemGroup>

Expand Down
282 changes: 282 additions & 0 deletions SaveHere/SaveHere.Tests/Services/HttpRequestAuthenticatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
using Xunit;
using SaveHere.Helpers;
using SaveHere.Models;
using System.Net.Http;
using System.Text;

namespace SaveHere.Tests.Services
{
public class HttpRequestAuthenticatorTests
{
[Fact]
public void ApplyAuthentication_BasicAuth_SetsAuthorizationHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.BasicAuth,
AuthUsername = "testuser",
AuthPassword = "testpass"
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.NotNull(request.Headers.Authorization);
Assert.Equal("Basic", request.Headers.Authorization.Scheme);

// Verify the credentials are correctly encoded
var expectedCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("testuser:testpass"));
Assert.Equal(expectedCredentials, request.Headers.Authorization.Parameter);
}

[Fact]
public void ApplyAuthentication_BasicAuth_WithEmptyPassword_SetsAuthorizationHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.BasicAuth,
AuthUsername = "testuser",
AuthPassword = null
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.NotNull(request.Headers.Authorization);
Assert.Equal("Basic", request.Headers.Authorization.Scheme);

// Verify the credentials are correctly encoded with empty password
var expectedCredentials = Convert.ToBase64String(Encoding.UTF8.GetBytes("testuser:"));
Assert.Equal(expectedCredentials, request.Headers.Authorization.Parameter);
}

[Fact]
public void ApplyAuthentication_BasicAuth_WithEmptyUsername_DoesNotSetHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.BasicAuth,
AuthUsername = "",
AuthPassword = "testpass"
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.Null(request.Headers.Authorization);
}

[Fact]
public void ApplyAuthentication_BearerToken_SetsAuthorizationHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.BearerToken,
AuthBearerToken = "my-jwt-token-12345"
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.NotNull(request.Headers.Authorization);
Assert.Equal("Bearer", request.Headers.Authorization.Scheme);
Assert.Equal("my-jwt-token-12345", request.Headers.Authorization.Parameter);
}

[Fact]
public void ApplyAuthentication_BearerToken_WithEmptyToken_DoesNotSetHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.BearerToken,
AuthBearerToken = ""
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.Null(request.Headers.Authorization);
}

[Fact]
public void ApplyAuthentication_Cookie_SetsCookieHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.Cookie,
AuthCookies = "session_id=abc123; user_token=xyz789"
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.True(request.Headers.Contains("Cookie"));
var cookieValues = request.Headers.GetValues("Cookie");
Assert.Contains("session_id=abc123; user_token=xyz789", cookieValues);
}

[Fact]
public void ApplyAuthentication_Cookie_WithEmptyCookies_DoesNotSetHeader()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.Cookie,
AuthCookies = ""
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.False(request.Headers.Contains("Cookie"));
}

[Fact]
public void ApplyAuthentication_CustomHeaders_SetsMultipleHeaders()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.CustomHeaders,
AuthCustomHeaders = "{\"X-Api-Key\": \"my-api-key\", \"X-Custom-Auth\": \"custom-value\"}"
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.True(request.Headers.Contains("X-Api-Key"));
Assert.True(request.Headers.Contains("X-Custom-Auth"));
Assert.Equal("my-api-key", request.Headers.GetValues("X-Api-Key").First());
Assert.Equal("custom-value", request.Headers.GetValues("X-Custom-Auth").First());
}

[Fact]
public void ApplyAuthentication_CustomHeaders_WithInvalidJson_DoesNotThrow()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.CustomHeaders,
AuthCustomHeaders = "not valid json"
};

// Act & Assert - should not throw
var exception = Record.Exception(() => HttpRequestAuthenticator.ApplyAuthentication(request, queueItem));
Assert.Null(exception);
}

[Fact]
public void ApplyAuthentication_CustomHeaders_WithEmptyHeaders_DoesNotThrow()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.CustomHeaders,
AuthCustomHeaders = ""
};

// Act & Assert - should not throw
var exception = Record.Exception(() => HttpRequestAuthenticator.ApplyAuthentication(request, queueItem));
Assert.Null(exception);
}

[Fact]
public void ApplyAuthentication_None_DoesNotModifyRequest()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.None
};

// Act
HttpRequestAuthenticator.ApplyAuthentication(request, queueItem);

// Assert
Assert.Null(request.Headers.Authorization);
Assert.False(request.Headers.Contains("Cookie"));
}

[Fact]
public void ApplyAuthentication_NullRequest_DoesNotThrow()
{
// Arrange
var queueItem = new FileDownloadQueueItem
{
InputUrl = "https://example.com/file.zip",
AuthType = AuthenticationType.BasicAuth,
AuthUsername = "user",
AuthPassword = "pass"
};

// Act & Assert - should not throw
var exception = Record.Exception(() => HttpRequestAuthenticator.ApplyAuthentication(null!, queueItem));
Assert.Null(exception);
}

[Fact]
public void ApplyAuthentication_NullQueueItem_DoesNotThrow()
{
// Arrange
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com/file.zip");

// Act & Assert - should not throw
var exception = Record.Exception(() => HttpRequestAuthenticator.ApplyAuthentication(request, null!));
Assert.Null(exception);
}

[Theory]
[InlineData(AuthenticationType.BasicAuth, true)]
[InlineData(AuthenticationType.BearerToken, true)]
[InlineData(AuthenticationType.Cookie, true)]
[InlineData(AuthenticationType.CustomHeaders, true)]
[InlineData(AuthenticationType.None, false)]
public void HasAuthentication_ReturnsCorrectValue_ForAuthType(AuthenticationType authType, bool expected)
{
// Arrange
var queueItem = new FileDownloadQueueItem
{
AuthType = authType
};

// Assert
Assert.Equal(expected, queueItem.HasAuthentication);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@implements IAsyncDisposable

Check warning on line 1 in SaveHere/SaveHere/Components/Download/DownloadFromDirectLink.razor

View workflow job for this annotation

GitHub Actions / Code Analysis

Illegal Attribute 'ColSpan' on 'MudTd' using pattern 'LowerCase' source location '(3238,28)-(3238,81)' (https://mudblazor.com/features/analyzers)

@using Microsoft.EntityFrameworkCore
@using SaveHere.Components.Utility
Expand Down Expand Up @@ -136,6 +136,68 @@
<MudIcon Icon="@Icons.Material.Filled.Info" Size="Size.Small" />
</MudTooltip>
</MudStack>

<MudDivider Class="my-3" />

<MudText Typo="Typo.subtitle2" Color="Color.Primary">Authentication Settings</MudText>

<MudStack Row>
<MudSelect T="AuthenticationType" @bind-Value="_authType" Label="Authentication Type" Variant="Variant.Outlined" Margin="Margin.Dense">
<MudSelectItem Value="@AuthenticationType.None">None</MudSelectItem>
<MudSelectItem Value="@AuthenticationType.BasicAuth">Basic Auth</MudSelectItem>
<MudSelectItem Value="@AuthenticationType.BearerToken">Bearer Token</MudSelectItem>
<MudSelectItem Value="@AuthenticationType.Cookie">Cookies</MudSelectItem>
<MudSelectItem Value="@AuthenticationType.CustomHeaders">Custom Headers</MudSelectItem>
</MudSelect>
<MudTooltip Text="Select authentication method if the download requires login.">
<MudIcon Icon="@Icons.Material.Filled.Info" Size="Size.Small" />
</MudTooltip>
</MudStack>

@if (_authType == AuthenticationType.BasicAuth)
{
<MudGrid>
<MudItem xs="12" sm="6">
<MudTextField @bind-Value="_authUsername"
Label="Username"
Variant="Variant.Outlined"
Margin="Margin.Dense" />
</MudItem>
<MudItem xs="12" sm="6">
<MudTextField @bind-Value="_authPassword"
Label="Password"
InputType="InputType.Password"
Variant="Variant.Outlined"
Margin="Margin.Dense" />
</MudItem>
</MudGrid>
}
else if (_authType == AuthenticationType.BearerToken)
{
<MudTextField @bind-Value="_authBearerToken"
Label="Bearer Token"
Variant="Variant.Outlined"
Margin="Margin.Dense"
HelperText="Enter your OAuth/JWT token" />
}
else if (_authType == AuthenticationType.Cookie)
{
<MudTextField @bind-Value="_authCookies"
Label="Cookies"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Lines="2"
HelperText="Format: name1=value1; name2=value2" />
}
else if (_authType == AuthenticationType.CustomHeaders)
{
<MudTextField @bind-Value="_authCustomHeaders"
Label="Custom Headers (JSON)"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Lines="3"
HelperText="JSON format: {&quot;Header-Name&quot;: &quot;value&quot;, &quot;Another-Header&quot;: &quot;value2&quot;}" />
}
</MudStack>
</MudExpansionPanel>
</MudExpansionPanels>
Expand Down Expand Up @@ -354,6 +416,14 @@
private bool _useHttp2 = true;
private bool _enableCompression = true;

// Authentication settings
private AuthenticationType _authType = AuthenticationType.None;
private string? _authUsername;
private string? _authPassword;
private string? _authBearerToken;
private string? _authCookies;
private string? _authCustomHeaders;

private readonly ChartOptions _chartOptions = new()
{
LineStrokeWidth = 3,
Expand Down Expand Up @@ -612,7 +682,13 @@
ParallelConnections = _parallelConnections,
BufferSizeKB = _bufferSizeKB,
UseHttp2 = _useHttp2,
EnableCompression = _enableCompression
EnableCompression = _enableCompression,
AuthType = _authType,
AuthUsername = _authUsername,
AuthPassword = _authPassword,
AuthBearerToken = _authBearerToken,
AuthCookies = _authCookies,
AuthCustomHeaders = _authCustomHeaders
};
_context.FileDownloadQueueItems.Add(newFileDownload);
await _context.SaveChangesAsync();
Expand Down
Loading
Loading