Compare commits

...

12 Commits

Author SHA1 Message Date
Bogdan
10d8f345c1 Display naming example errors when all fields are empty
(cherry picked from commit 768af433d1655c587a9eee9b100f306ba4345f88)
2024-09-28 05:18:41 +03:00
Robin Dadswell
fb720b8714 Fixed: Telegram log message including token
(cherry picked from commit a7cb264cc8013d9a56aee7d5e41acfd76cde5f96)
2024-09-28 05:18:29 +03:00
Servarr
e8131b5791 Automated API Docs update 2024-09-25 10:34:30 +03:00
Bogdan
4f793f6b93 Remove $ from Discord delete notifications 2024-09-25 10:28:00 +03:00
Bogdan
4215c21c94 Add package needed for RemoveDiacritics 2024-09-23 05:46:26 +03:00
Paul DiLoreto
6913789adc New: Use instance name in forms authentication cookie name (#3761)
(cherry picked from commit 97ebaf279650082c6baee9563ef179921c5ed25a)
(cherry picked from commit faf9173b3b4a298e3afa9a186e66ba6764ac055e)
(cherry picked from commit 75fae9262c6ca003d24df9fcf035d75b1e90f994)

---------

Co-authored-by: Mark McDowall <mark@mcdowall.ca>
2024-09-23 05:45:01 +03:00
Mark McDowall
09e0c40792 Fixed: Limit redirects after login to local paths
(cherry picked from commit 14005d8d1054eafaba808337a109d5812f3e79e6)
2024-09-23 05:41:21 +03:00
Mark McDowall
baff805551 New: Return downloading magnets from Transmission
(cherry picked from commit 11a9dcb3890eaf99602900f37e64007f2fbf9b8e)
2024-09-23 05:40:25 +03:00
Bogdan
c885fe43cd Fix disabled style for monitor toggle button
(cherry picked from commit dde28cbd7e16b85f78d38c8dde7cf6bbb6119bb3)
2024-09-23 05:39:36 +03:00
Treycos
464a777722 Updated code action fixall value for VSCode
(cherry picked from commit 8af4246ff9baee4c291550102769a1186f65dc29)
2024-09-23 05:39:19 +03:00
momo
89e5999c85 Fix description for API key as query parameter
(cherry picked from commit 30c36fdc3baa686102ff124833c7963fc786f251)
2024-09-23 05:36:05 +03:00
Bogdan
b6fa332550 Ignore metadata tests temporarily once again 2024-09-23 05:35:27 +03:00
24 changed files with 160 additions and 134 deletions

View File

@@ -9,7 +9,7 @@
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll": true
"source.fixAll": "explicit"
},
"typescript.preferences.quoteStyle": "single",

View File

@@ -3,9 +3,9 @@
padding: 0;
font-size: inherit;
}
.isDisabled {
color: var(--disabledColor);
cursor: not-allowed;
&.isDisabled {
color: var(--disabledColor);
cursor: not-allowed;
}
}

View File

@@ -1,4 +1,3 @@
import _ from 'lodash';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';
@@ -15,11 +14,11 @@ function createMapStateToProps() {
(state) => state.settings.advancedSettings,
(state) => state.settings.namingExamples,
createSettingsSectionSelector(SECTION),
(advancedSettings, examples, sectionSettings) => {
(advancedSettings, namingExamples, sectionSettings) => {
return {
advancedSettings,
examples: examples.item,
examplesPopulated: !_.isEmpty(examples.item),
examples: namingExamples.item,
examplesPopulated: namingExamples.isPopulated,
...sectionSettings
};
}

View File

@@ -4,6 +4,7 @@
<PackageVersion Include="AutoFixture" Version="4.17.0" />
<PackageVersion Include="coverlet.collector" Version="3.0.4-preview.27.ge7cb7c3b40" PrivateAssets="all" />
<PackageVersion Include="Dapper" Version="2.0.151" />
<PackageVersion Include="Diacritical.Net" Version="1.0.4" />
<PackageVersion Include="DryIoc.dll" Version="5.4.3" />
<PackageVersion Include="DryIoc.Microsoft.DependencyInjection" Version="6.2.0" />
<PackageVersion Include="Equ" Version="2.3.0" />
@@ -65,4 +66,4 @@
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />
<PackageVersion Include="TagLibSharp-Lidarr" Version="2.2.0.19" />
</ItemGroup>
</Project>
</Project>

View File

@@ -89,6 +89,10 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase(@"https://discord.com/api/webhooks/mySecret")]
[TestCase(@"https://discord.com/api/webhooks/mySecret/01233210")]
// Telegram
[TestCase(@"https://api.telegram.org/bot1234567890:mySecret/sendmessage: chat_id=123456&parse_mode=HTML&text=<text>")]
[TestCase(@"https://api.telegram.org/bot1234567890:mySecret/")]
public void should_clean_message(string message)
{
var cleansedMessage = CleanseLogMessage.Cleanse(message);

View File

@@ -54,7 +54,10 @@ namespace NzbDrone.Common.Instrumentation
new (@"api/v[0-9]/notification/readarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Discord
new (@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
new (@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Telegram
new (@"api.telegram.org/bot(?<id>[\d]+):(?<secret>[\w-]+)/", RegexOptions.Compiled | RegexOptions.IgnoreCase)
};
private static readonly Regex CleanseRemoteIPRegex = new (@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);

View File

@@ -49,10 +49,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.TransmissionTests
}
[Test]
public void magnet_download_should_not_return_the_item()
public void magnet_download_should_be_returned_as_queued()
{
PrepareClientToReturnMagnetItem();
Subject.GetItems().Count().Should().Be(0);
var item = Subject.GetItems().Single();
item.Status.Should().Be(DownloadItemStatus.Queued);
}
[Test]

View File

@@ -60,7 +60,10 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.VuzeTests
public void magnet_download_should_not_return_the_item()
{
PrepareClientToReturnMagnetItem();
Subject.GetItems().Count().Should().Be(0);
var item = Subject.GetItems().Single();
item.Status.Should().Be(DownloadItemStatus.Queued);
}
[Test]

View File

@@ -13,7 +13,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.MetadataSource.Goodreads
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class BookInfoProxyFixture : CoreTest<BookInfoProxy>
{
private MetadataProfile _metadataProfile;

View File

@@ -15,7 +15,7 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MetadataSource.Goodreads
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class BookInfoProxySearchFixture : CoreTest<BookInfoProxy>
{
[SetUp]

View File

@@ -41,12 +41,6 @@ namespace NzbDrone.Core.Download.Clients.Transmission
foreach (var torrent in torrents)
{
// If totalsize == 0 the torrent is a magnet downloading metadata
if (torrent.TotalSize == 0)
{
continue;
}
var outputPath = new OsPath(torrent.DownloadDir);
if (Settings.TvDirectory.IsNotNullOrWhiteSpace())
@@ -97,6 +91,10 @@ namespace NzbDrone.Core.Download.Clients.Transmission
item.Status = DownloadItemStatus.Warning;
item.Message = torrent.ErrorString;
}
else if (torrent.TotalSize == 0)
{
item.Status = DownloadItemStatus.Queued;
}
else if (torrent.LeftUntilDone == 0 && (torrent.Status == TransmissionTorrentStatus.Stopped ||
torrent.Status == TransmissionTorrentStatus.Seeding ||
torrent.Status == TransmissionTorrentStatus.SeedingWait))

View File

@@ -25,15 +25,15 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnGrab(GrabMessage message)
{
var embeds = new List<Embed>
{
new Embed
{
Description = message.Message,
Title = message.Author.Name,
Text = message.Message,
Color = (int)DiscordColors.Warning
}
};
{
new ()
{
Description = message.Message,
Title = message.Author.Name,
Text = message.Message,
Color = (int)DiscordColors.Warning
}
};
var payload = CreatePayload($"Grabbed: {message.Message}", embeds);
_proxy.SendPayload(payload, Settings);
@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Notifications.Discord
{
var attachments = new List<Embed>
{
new Embed
new ()
{
Description = message.Message,
Title = message.Author.Name,
@@ -59,12 +59,12 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnRename(Author author, List<RenamedBookFile> renamedFiles)
{
var attachments = new List<Embed>
{
new Embed
{
Title = author.Name,
}
};
{
new ()
{
Title = author.Name,
}
};
var payload = CreatePayload("Renamed", attachments);
@@ -74,21 +74,21 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnAuthorAdded(Author author)
{
var attachments = new List<Embed>
{
new Embed
{
Title = author.Name,
Fields = new List<DiscordField>()
{
new DiscordField()
{
Name = "Links",
Value = string.Join(" / ", author.Metadata.Value.Links.Select(link => $"[{link.Name}]({link.Url})"))
}
},
}
};
var payload = CreatePayload($"Author Added", attachments);
{
new ()
{
Title = author.Name,
Fields = new List<DiscordField>()
{
new ()
{
Name = "Links",
Value = string.Join(" / ", author.Metadata.Value.Links.Select(link => $"[{link.Name}]({link.Url})"))
}
},
}
};
var payload = CreatePayload("Author Added", attachments);
_proxy.SendPayload(payload, Settings);
}
@@ -96,13 +96,13 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnAuthorDelete(AuthorDeleteMessage deleteMessage)
{
var attachments = new List<Embed>
{
new Embed
{
Title = deleteMessage.Author.Name,
Description = deleteMessage.DeletedFilesMessage
}
};
{
new ()
{
Title = deleteMessage.Author.Name,
Description = deleteMessage.DeletedFilesMessage
}
};
var payload = CreatePayload("Author Deleted", attachments);
@@ -112,13 +112,13 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnBookDelete(BookDeleteMessage deleteMessage)
{
var attachments = new List<Embed>
{
new Embed
{
Title = $"${deleteMessage.Book.Author.Value.Name} - ${deleteMessage.Book.Title}",
Description = deleteMessage.DeletedFilesMessage
}
};
{
new ()
{
Title = $"{deleteMessage.Book.Author.Value.Name} - ${deleteMessage.Book.Title}",
Description = deleteMessage.DeletedFilesMessage
}
};
var payload = CreatePayload("Book Deleted", attachments);
@@ -128,13 +128,13 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnBookFileDelete(BookFileDeleteMessage deleteMessage)
{
var attachments = new List<Embed>
{
new Embed
{
Title = $"${deleteMessage.Book.Author.Value.Name} - ${deleteMessage.Book.Title} - file deleted",
Description = deleteMessage.BookFile.Path
}
};
{
new ()
{
Title = $"{deleteMessage.Book.Author.Value.Name} - ${deleteMessage.Book.Title} - file deleted",
Description = deleteMessage.BookFile.Path
}
};
var payload = CreatePayload("Book File Deleted", attachments);
@@ -144,14 +144,14 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
var attachments = new List<Embed>
{
new Embed
{
Title = healthCheck.Source.Name,
Text = healthCheck.Message,
Color = healthCheck.Type == HealthCheck.HealthCheckResult.Warning ? (int)DiscordColors.Warning : (int)DiscordColors.Danger
}
};
{
new ()
{
Title = healthCheck.Source.Name,
Text = healthCheck.Message,
Color = healthCheck.Type == HealthCheck.HealthCheckResult.Warning ? (int)DiscordColors.Warning : (int)DiscordColors.Danger
}
};
var payload = CreatePayload("Health Issue", attachments);
@@ -161,13 +161,13 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnBookRetag(BookRetagMessage message)
{
var attachments = new List<Embed>
{
new Embed
{
Title = BOOK_RETAGGED_TITLE,
Text = message.Message
}
};
{
new ()
{
Title = BOOK_RETAGGED_TITLE,
Text = message.Message
}
};
var payload = CreatePayload($"Track file tags updated: {message.Message}", attachments);
@@ -178,7 +178,7 @@ namespace NzbDrone.Core.Notifications.Discord
{
var attachments = new List<Embed>
{
new Embed
new ()
{
Description = message.Message,
Title = message.SourceTitle,
@@ -195,7 +195,7 @@ namespace NzbDrone.Core.Notifications.Discord
{
var attachments = new List<Embed>
{
new Embed
new ()
{
Description = message.Message,
Title = message.Book?.Title ?? message.Message,
@@ -211,32 +211,32 @@ namespace NzbDrone.Core.Notifications.Discord
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
{
var attachments = new List<Embed>
{
new Embed
{
Author = new DiscordAuthor
{
Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author,
IconUrl = "https://raw.githubusercontent.com/Readarr/Readarr/develop/Logo/256.png"
},
Title = APPLICATION_UPDATE_TITLE,
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
Color = (int)DiscordColors.Standard,
Fields = new List<DiscordField>()
{
new DiscordField()
{
Name = "Previous Version",
Value = updateMessage.PreviousVersion.ToString()
},
new DiscordField()
{
Name = "New Version",
Value = updateMessage.NewVersion.ToString()
}
},
}
};
{
new ()
{
Author = new DiscordAuthor
{
Name = Settings.Author.IsNullOrWhiteSpace() ? Environment.MachineName : Settings.Author,
IconUrl = "https://raw.githubusercontent.com/Readarr/Readarr/develop/Logo/256.png"
},
Title = APPLICATION_UPDATE_TITLE,
Timestamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
Color = (int)DiscordColors.Standard,
Fields = new List<DiscordField>()
{
new ()
{
Name = "Previous Version",
Value = updateMessage.PreviousVersion.ToString()
},
new ()
{
Name = "New Version",
Value = updateMessage.NewVersion.ToString()
}
},
}
};
var payload = CreatePayload(null, attachments);

View File

@@ -4,6 +4,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" />
<PackageReference Include="Diacritical.Net" />
<PackageReference Include="LazyCache" />
<PackageReference Include="Polly" />
<PackageReference Include="System.Text.Json" />

View File

@@ -135,7 +135,7 @@ namespace NzbDrone.Host
Name = "apikey",
Type = SecuritySchemeType.ApiKey,
Scheme = "apiKey",
Description = "Apikey passed as header",
Description = "Apikey passed as query parameter",
In = ParameterLocation.Query,
Reference = new OpenApiReference
{

View File

@@ -7,7 +7,7 @@ using Readarr.Api.V1.Author;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class AuthorEditorFixture : IntegrationTest
{
private void GivenExistingAuthor()

View File

@@ -7,7 +7,7 @@ using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class AuthorFixture : IntegrationTest
{
[Test]

View File

@@ -4,7 +4,7 @@ using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class AuthorLookupFixture : IntegrationTest
{
[TestCase("Robert Harris", "Robert Harris")]

View File

@@ -6,7 +6,7 @@ using Readarr.Api.V1.Blocklist;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class BlocklistFixture : IntegrationTest
{
private AuthorResource _author;

View File

@@ -9,7 +9,7 @@ using Readarr.Api.V1.Books;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class CalendarFixture : IntegrationTest
{
public ClientBase<BookResource> Calendar;

View File

@@ -8,7 +8,7 @@ using Readarr.Api.V1.RootFolders;
namespace NzbDrone.Integration.Test.ApiTests.WantedTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class CutoffUnmetFixture : IntegrationTest
{
[SetUp]

View File

@@ -7,7 +7,7 @@ using Readarr.Api.V1.RootFolders;
namespace NzbDrone.Integration.Test.ApiTests.WantedTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2024-08-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2024-12-15 00:00:00Z")]
public class MissingFixture : IntegrationTest
{
[SetUp]

View File

@@ -13815,7 +13815,7 @@
},
"apikey": {
"type": "apiKey",
"description": "Apikey passed as header",
"description": "Apikey passed as query parameter",
"name": "apikey",
"in": "query"
}

View File

@@ -1,12 +1,18 @@
using System;
using System.Text.RegularExpressions;
using Diacritical;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.DependencyInjection;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Configuration;
namespace Readarr.Http.Authentication
{
public static class AuthenticationBuilderExtensions
{
private static readonly Regex CookieNameRegex = new Regex(@"[^a-z0-9]+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder authenticationBuilder, string name, Action<ApiKeyAuthenticationOptions> options)
{
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
@@ -29,19 +35,27 @@ namespace Readarr.Http.Authentication
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services)
{
return services.AddAuthentication()
.AddNone(AuthenticationType.None.ToString())
.AddExternal(AuthenticationType.External.ToString())
.AddBasic(AuthenticationType.Basic.ToString())
.AddCookie(AuthenticationType.Forms.ToString(), options =>
services.AddOptions<CookieAuthenticationOptions>(AuthenticationType.Forms.ToString())
.Configure<IConfigFileProvider>((options, configFileProvider) =>
{
options.Cookie.Name = "ReadarrAuth";
// Replace diacritics and replace non-word characters to ensure cookie name doesn't contain any valid URL characters not allowed in cookie names
var instanceName = configFileProvider.InstanceName;
instanceName = instanceName.RemoveDiacritics();
instanceName = CookieNameRegex.Replace(instanceName, string.Empty);
options.Cookie.Name = $"{instanceName}Auth";
options.AccessDeniedPath = "/login?loginFailed=true";
options.LoginPath = "/login";
options.ExpireTimeSpan = TimeSpan.FromDays(7);
options.SlidingExpiration = true;
options.ReturnUrlParameter = "returnUrl";
})
});
return services.AddAuthentication()
.AddNone(AuthenticationType.None.ToString())
.AddExternal(AuthenticationType.External.ToString())
.AddBasic(AuthenticationType.Basic.ToString())
.AddCookie(AuthenticationType.Forms.ToString())
.AddApiKey("API", options =>
{
options.HeaderName = "X-Api-Key";

View File

@@ -47,7 +47,7 @@ namespace Readarr.Http.Authentication
await HttpContext.SignInAsync(AuthenticationType.Forms.ToString(), new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties);
if (returnUrl.IsNullOrWhiteSpace())
if (returnUrl.IsNullOrWhiteSpace() || !Url.IsLocalUrl(returnUrl))
{
return Redirect(_configFileProvider.UrlBase + "/");
}