mirror of
https://github.com/Readarr/Readarr.git
synced 2026-03-17 16:14:04 -04:00
Compare commits
12 Commits
sonarr-pul
...
v0.4.0.263
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d8f345c1 | ||
|
|
fb720b8714 | ||
|
|
e8131b5791 | ||
|
|
4f793f6b93 | ||
|
|
4215c21c94 | ||
|
|
6913789adc | ||
|
|
09e0c40792 | ||
|
|
baff805551 | ||
|
|
c885fe43cd | ||
|
|
464a777722 | ||
|
|
89e5999c85 | ||
|
|
b6fa332550 |
2
frontend/.vscode/settings.json
vendored
2
frontend/.vscode/settings.json
vendored
@@ -9,7 +9,7 @@
|
||||
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
"source.fixAll": "explicit"
|
||||
},
|
||||
|
||||
"typescript.preferences.quoteStyle": "single",
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.isDisabled {
|
||||
color: var(--disabledColor);
|
||||
cursor: not-allowed;
|
||||
&.isDisabled {
|
||||
color: var(--disabledColor);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" />
|
||||
<PackageReference Include="Diacritical.Net" />
|
||||
<PackageReference Include="LazyCache" />
|
||||
<PackageReference Include="Polly" />
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -13815,7 +13815,7 @@
|
||||
},
|
||||
"apikey": {
|
||||
"type": "apiKey",
|
||||
"description": "Apikey passed as header",
|
||||
"description": "Apikey passed as query parameter",
|
||||
"name": "apikey",
|
||||
"in": "query"
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 + "/");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user