diff --git a/NzbDrone.Api/Authentication/AuthenticationValidator.cs b/NzbDrone.Api/Authentication/AuthenticationValidator.cs new file mode 100644 index 000000000..c979e1467 --- /dev/null +++ b/NzbDrone.Api/Authentication/AuthenticationValidator.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Nancy.Authentication.Basic; +using Nancy.Security; +using NzbDrone.Common; + +namespace NzbDrone.Api.Authentication +{ + public class AuthenticationValidator : IUserValidator + { + private readonly IConfigFileProvider _configFileProvider; + + public AuthenticationValidator(IConfigFileProvider configFileProvider) + { + _configFileProvider = configFileProvider; + } + + public IUserIdentity Validate(string username, string password) + { + if (_configFileProvider.BasicAuthUsername.Equals(username) && + _configFileProvider.BasicAuthPassword.Equals(password)) + { + return new NzbDroneUser { UserName = username}; + } + + return null; + } + } +} diff --git a/NzbDrone.Api/Authentication/NzbDroneUser.cs b/NzbDrone.Api/Authentication/NzbDroneUser.cs new file mode 100644 index 000000000..56d7883de --- /dev/null +++ b/NzbDrone.Api/Authentication/NzbDroneUser.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Nancy.Security; + +namespace NzbDrone.Api.Authentication +{ + public class NzbDroneUser : IUserIdentity + { + public string UserName { get; set; } + + public IEnumerable Claims { get; set; } + } +} diff --git a/NzbDrone.Api/ErrorManagement/ErrorHandler.cs b/NzbDrone.Api/ErrorManagement/ErrorHandler.cs index 154dd8bd4..6976b6fb0 100644 --- a/NzbDrone.Api/ErrorManagement/ErrorHandler.cs +++ b/NzbDrone.Api/ErrorManagement/ErrorHandler.cs @@ -23,6 +23,9 @@ namespace NzbDrone.Api.ErrorManagement return; } + if (statusCode == HttpStatusCode.Unauthorized) + return; + if (context.Response.ContentType == "text/html" || context.Response.ContentType == "text/plain") context.Response = new ErrorModel { diff --git a/NzbDrone.Api/Frontend/IndexModule.cs b/NzbDrone.Api/Frontend/IndexModule.cs index 042cae893..eb3b1f5c6 100644 --- a/NzbDrone.Api/Frontend/IndexModule.cs +++ b/NzbDrone.Api/Frontend/IndexModule.cs @@ -1,5 +1,6 @@ using System; using Nancy; +using Nancy.Security; namespace NzbDrone.Api.Frontend { @@ -7,6 +8,7 @@ namespace NzbDrone.Api.Frontend { public IndexModule() { + this.RequiresAuthentication(); //Serve anything that doesn't have an extension Get[@"/(.*)"] = x => Index(); } diff --git a/NzbDrone.Api/NancyBootstrapper.cs b/NzbDrone.Api/NancyBootstrapper.cs index f31528274..f338dd079 100644 --- a/NzbDrone.Api/NancyBootstrapper.cs +++ b/NzbDrone.Api/NancyBootstrapper.cs @@ -1,12 +1,17 @@ -using NLog; +using System; +using NLog; +using Nancy; +using Nancy.Authentication.Basic; using Nancy.Bootstrapper; using Nancy.Conventions; using Nancy.Diagnostics; using NzbDrone.Api.ErrorManagement; using NzbDrone.Api.Extensions; using NzbDrone.Api.Frontend; +using NzbDrone.Common; using NzbDrone.Common.Composition; using NzbDrone.Common.Messaging; +using NzbDrone.Common.Model; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Lifecycle; using TinyIoC; @@ -33,6 +38,13 @@ namespace NzbDrone.Api container.Resolve().Register(); container.Resolve().PublishEvent(new ApplicationStartedEvent()); + + if (container.Resolve().AuthenticationType == AuthenticationType.Basic) + { + pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration( + container.Resolve(), + "NzbDrone")); + } ApplicationPipelines.OnError.AddItemToEndOfPipeline(container.Resolve().HandleException); } diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 52ced19fc..54785dcc3 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -70,6 +70,9 @@ False ..\packages\Nancy.0.16.1\lib\net40\Nancy.dll + + ..\packages\Nancy.Authentication.Basic.0.16.1\lib\net40\Nancy.Authentication.Basic.dll + False ..\packages\Newtonsoft.Json.5.0.3\lib\net35\Newtonsoft.Json.dll @@ -86,6 +89,8 @@ + + diff --git a/NzbDrone.Api/NzbDroneApiModule.cs b/NzbDrone.Api/NzbDroneApiModule.cs index aef3bca59..342c96d29 100644 --- a/NzbDrone.Api/NzbDroneApiModule.cs +++ b/NzbDrone.Api/NzbDroneApiModule.cs @@ -1,4 +1,5 @@ using Nancy; +using Nancy.Security; namespace NzbDrone.Api { @@ -7,6 +8,7 @@ namespace NzbDrone.Api protected NzbDroneApiModule(string resource) : base("/api/" + resource.Trim('/')) { + this.RequiresAuthentication(); Options["/"] = x => new Response(); } } diff --git a/NzbDrone.Api/NzbDroneRestModule.cs b/NzbDrone.Api/NzbDroneRestModule.cs index 87507b234..19efa7881 100644 --- a/NzbDrone.Api/NzbDroneRestModule.cs +++ b/NzbDrone.Api/NzbDroneRestModule.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Nancy.Security; using NzbDrone.Api.REST; using NzbDrone.Api.Validation; using NzbDrone.Core.Datastore; @@ -12,7 +13,7 @@ namespace NzbDrone.Api protected NzbDroneRestModule() : this(new TResource().ResourceName) { - + this.RequiresAuthentication(); } protected NzbDroneRestModule(string resource) diff --git a/NzbDrone.Api/Series/SeriesModule.cs b/NzbDrone.Api/Series/SeriesModule.cs index c934ee62d..be648aad0 100644 --- a/NzbDrone.Api/Series/SeriesModule.cs +++ b/NzbDrone.Api/Series/SeriesModule.cs @@ -32,9 +32,6 @@ namespace NzbDrone.Api.Series SharedValidator.RuleFor(s => s.RootFolderId).ValidId(); SharedValidator.RuleFor(s => s.QualityProfileId).ValidId(); - - - PostValidator.RuleFor(s => s.Title).NotEmpty(); } diff --git a/NzbDrone.Api/packages.config b/NzbDrone.Api/packages.config index d3fe03882..33724ffc8 100644 --- a/NzbDrone.Api/packages.config +++ b/NzbDrone.Api/packages.config @@ -4,6 +4,7 @@ + diff --git a/NzbDrone.Common/IConfigFileProvider.cs b/NzbDrone.Common/IConfigFileProvider.cs index 2d5bf6d7a..7fe7e30eb 100644 --- a/NzbDrone.Common/IConfigFileProvider.cs +++ b/NzbDrone.Common/IConfigFileProvider.cs @@ -12,6 +12,8 @@ namespace NzbDrone.Common int Port { get; set; } bool LaunchBrowser { get; set; } AuthenticationType AuthenticationType { get; set; } + string BasicAuthUsername { get; set; } + string BasicAuthPassword { get; set; } int GetValueInt(string key, int defaultValue); bool GetValueBoolean(string key, bool defaultValue); string GetValue(string key, object defaultValue); @@ -32,7 +34,6 @@ namespace NzbDrone.Common CreateDefaultConfigFile(); } - public virtual Guid Guid { get @@ -61,10 +62,21 @@ namespace NzbDrone.Common public virtual AuthenticationType AuthenticationType { - get { return (AuthenticationType)GetValueInt("AuthenticationType", 0); } - set { SetValue("AuthenticationType", (int)value); } + get { return GetValueEnum("AuthenticationType", AuthenticationType.Anonymous); } + set { SetValue("AuthenticationType", value); } } + public virtual string BasicAuthUsername + { + get { return GetValue("BasicAuthUsername", ""); } + set { SetValue("BasicAuthUsername", value); } + } + + public virtual string BasicAuthPassword + { + get { return GetValue("BasicAuthPassword", ""); } + set { SetValue("BasicAuthPassword", value); } + } public virtual int GetValueInt(string key, int defaultValue) { @@ -76,6 +88,11 @@ namespace NzbDrone.Common return Convert.ToBoolean(GetValue(key, defaultValue)); } + private T GetValueEnum(string key, T defaultValue) + { + return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), true); + } + public virtual string GetValue(string key, object defaultValue) { var xDoc = XDocument.Load(_configFile); @@ -96,7 +113,6 @@ namespace NzbDrone.Common return defaultValue.ToString(); } - public virtual void SetValue(string key, object value) { var xDoc = XDocument.Load(_configFile); @@ -115,6 +131,11 @@ namespace NzbDrone.Common xDoc.Save(_configFile); } + private void SetValue(string key, Enum value) + { + SetValue(key, value.ToString().ToLower()); + } + private void CreateDefaultConfigFile() { if (!File.Exists(_configFile)) diff --git a/NzbDrone.Common/Model/AuthenticationType.cs b/NzbDrone.Common/Model/AuthenticationType.cs index 34d37edf6..31529b179 100644 --- a/NzbDrone.Common/Model/AuthenticationType.cs +++ b/NzbDrone.Common/Model/AuthenticationType.cs @@ -4,7 +4,7 @@ namespace NzbDrone.Common.Model { public enum AuthenticationType { - Anonymous = 0, - Windows = 1 + Anonymous, + Basic } } diff --git a/NzbDrone.Console/packages.config b/NzbDrone.Console/packages.config index f2a88a23a..d889a47ab 100644 --- a/NzbDrone.Console/packages.config +++ b/NzbDrone.Console/packages.config @@ -1,4 +1,6 @@  + + \ No newline at end of file