mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-09-17 17:14:18 +02:00
New: Remove Basic Auth
(cherry picked from commit 0f9e063e2146812f6e963363eee70a524612f354)
This commit is contained in:
@@ -30,7 +30,9 @@ export const authenticationMethodOptions = [
|
|||||||
key: 'basic',
|
key: 'basic',
|
||||||
get value() {
|
get value() {
|
||||||
return translate('AuthBasic');
|
return translate('AuthBasic');
|
||||||
}
|
},
|
||||||
|
isDisabled: true,
|
||||||
|
isHidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'forms',
|
key: 'forms',
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Authentication
|
namespace NzbDrone.Core.Authentication
|
||||||
{
|
{
|
||||||
public enum AuthenticationType
|
public enum AuthenticationType
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
[Obsolete("Use Forms authentication instead")]
|
||||||
Basic = 1,
|
Basic = 1,
|
||||||
Forms = 2,
|
Forms = 2,
|
||||||
External = 3
|
External = 3
|
||||||
|
@@ -207,8 +207,8 @@ namespace NzbDrone.Core.Configuration
|
|||||||
|
|
||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
SetValue("AuthenticationMethod", AuthenticationType.Basic);
|
SetValue("AuthenticationMethod", AuthenticationType.Forms);
|
||||||
return AuthenticationType.Basic;
|
return AuthenticationType.Forms;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Enum.TryParse<AuthenticationType>(_authOptions.Method, out var enumValue)
|
return Enum.TryParse<AuthenticationType>(_authOptions.Method, out var enumValue)
|
||||||
|
@@ -41,10 +41,14 @@ namespace Prowlarr.Api.V1.Config
|
|||||||
SharedValidator.RuleFor(c => c.UrlBase).ValidUrlBase();
|
SharedValidator.RuleFor(c => c.UrlBase).ValidUrlBase();
|
||||||
SharedValidator.RuleFor(c => c.InstanceName).ContainsProwlarr().When(c => c.InstanceName.IsNotNullOrWhiteSpace());
|
SharedValidator.RuleFor(c => c.InstanceName).ContainsProwlarr().When(c => c.InstanceName.IsNotNullOrWhiteSpace());
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationMethod == AuthenticationType.Basic ||
|
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationMethod == AuthenticationType.Forms);
|
||||||
c.AuthenticationMethod == AuthenticationType.Forms);
|
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationMethod == AuthenticationType.Forms);
|
||||||
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationMethod == AuthenticationType.Basic ||
|
|
||||||
c.AuthenticationMethod == AuthenticationType.Forms);
|
SharedValidator.RuleFor(c => c.AuthenticationMethod)
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
|
.NotEqual(AuthenticationType.Basic)
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
.WithMessage("'Basic' is no longer supported, switch to 'Forms' instead.");
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.PasswordConfirmation)
|
SharedValidator.RuleFor(c => c.PasswordConfirmation)
|
||||||
.Must((resource, p) => IsMatchingPassword(resource)).WithMessage("Must match Password");
|
.Must((resource, p) => IsMatchingPassword(resource)).WithMessage("Must match Password");
|
||||||
|
@@ -18,11 +18,6 @@ namespace Prowlarr.Http.Authentication
|
|||||||
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
|
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthenticationBuilder AddBasic(this AuthenticationBuilder authenticationBuilder, string name)
|
|
||||||
{
|
|
||||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>(name, options => { });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthenticationBuilder AddNone(this AuthenticationBuilder authenticationBuilder, string name)
|
public static AuthenticationBuilder AddNone(this AuthenticationBuilder authenticationBuilder, string name)
|
||||||
{
|
{
|
||||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(name, options => { });
|
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(name, options => { });
|
||||||
@@ -54,7 +49,9 @@ namespace Prowlarr.Http.Authentication
|
|||||||
return services.AddAuthentication()
|
return services.AddAuthentication()
|
||||||
.AddNone(AuthenticationType.None.ToString())
|
.AddNone(AuthenticationType.None.ToString())
|
||||||
.AddExternal(AuthenticationType.External.ToString())
|
.AddExternal(AuthenticationType.External.ToString())
|
||||||
.AddBasic(AuthenticationType.Basic.ToString())
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
|
.AddCookie(AuthenticationType.Basic.ToString())
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
.AddCookie(AuthenticationType.Forms.ToString())
|
.AddCookie(AuthenticationType.Forms.ToString())
|
||||||
.AddApiKey("API", options =>
|
.AddApiKey("API", options =>
|
||||||
{
|
{
|
||||||
|
@@ -1,84 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Core.Authentication;
|
|
||||||
|
|
||||||
namespace Prowlarr.Http.Authentication
|
|
||||||
{
|
|
||||||
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
|
||||||
{
|
|
||||||
private readonly IAuthenticationService _authService;
|
|
||||||
|
|
||||||
public BasicAuthenticationHandler(IAuthenticationService authService,
|
|
||||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
|
||||||
ILoggerFactory logger,
|
|
||||||
UrlEncoder encoder)
|
|
||||||
: base(options, logger, encoder)
|
|
||||||
{
|
|
||||||
_authService = authService;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
|
||||||
{
|
|
||||||
if (!Request.Headers.ContainsKey("Authorization"))
|
|
||||||
{
|
|
||||||
return Task.FromResult(AuthenticateResult.Fail("Authorization header missing."));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get authorization key
|
|
||||||
var authorizationHeader = Request.Headers["Authorization"].ToString();
|
|
||||||
var authHeaderRegex = new Regex(@"Basic (.*)");
|
|
||||||
|
|
||||||
if (!authHeaderRegex.IsMatch(authorizationHeader))
|
|
||||||
{
|
|
||||||
return Task.FromResult(AuthenticateResult.Fail("Authorization code not formatted properly."));
|
|
||||||
}
|
|
||||||
|
|
||||||
var authBase64 = Encoding.UTF8.GetString(Convert.FromBase64String(authHeaderRegex.Replace(authorizationHeader, "$1")));
|
|
||||||
var authSplit = authBase64.Split(':', 2);
|
|
||||||
var authUsername = authSplit[0];
|
|
||||||
var authPassword = authSplit.Length > 1 ? authSplit[1] : throw new Exception("Unable to get password");
|
|
||||||
|
|
||||||
var user = _authService.Login(Request, authUsername, authPassword);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
return Task.FromResult(AuthenticateResult.Fail("The username or password is not correct."));
|
|
||||||
}
|
|
||||||
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new Claim("user", user.Username),
|
|
||||||
new Claim("identifier", user.Identifier.ToString()),
|
|
||||||
new Claim("AuthType", AuthenticationType.Basic.ToString())
|
|
||||||
};
|
|
||||||
|
|
||||||
var identity = new ClaimsIdentity(claims, "Basic", "user", "identifier");
|
|
||||||
var principal = new ClaimsPrincipal(identity);
|
|
||||||
var ticket = new AuthenticationTicket(principal, "Basic");
|
|
||||||
|
|
||||||
return Task.FromResult(AuthenticateResult.Success(ticket));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
|
||||||
{
|
|
||||||
Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{BuildInfo.AppName}\"";
|
|
||||||
Response.StatusCode = 401;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
|
|
||||||
{
|
|
||||||
Response.StatusCode = 403;
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user