From de607e207b8c9baed0ba9769d6b87ce8a39bfded Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 20 Sep 2013 15:19:48 -0700 Subject: [PATCH] ApiKey Authentication cleanup --- .../Authentication/EnableBasicAuthInNancy.cs | 11 +++----- .../EnableStatelessAuthInNancy.cs | 26 ++++++++--------- NzbDrone.Api/Extensions/RequestExtensions.cs | 28 +++++++++++++++++++ .../Frontend/Mappers/IndexHtmlMapper.cs | 15 +--------- NzbDrone.Api/NancyBootstrapper.cs | 2 -- UI/Mixins/jquery.ajax.js | 7 ++--- UI/ServerStatus.js | 2 +- UI/index.html | 2 +- 8 files changed, 48 insertions(+), 45 deletions(-) create mode 100644 NzbDrone.Api/Extensions/RequestExtensions.cs diff --git a/NzbDrone.Api/Authentication/EnableBasicAuthInNancy.cs b/NzbDrone.Api/Authentication/EnableBasicAuthInNancy.cs index ab0bd9f60..a6994caf3 100644 --- a/NzbDrone.Api/Authentication/EnableBasicAuthInNancy.cs +++ b/NzbDrone.Api/Authentication/EnableBasicAuthInNancy.cs @@ -1,15 +1,12 @@ using Nancy; using Nancy.Authentication.Basic; using Nancy.Bootstrapper; +using NzbDrone.Api.Extensions; +using NzbDrone.Api.Extensions.Pipelines; namespace NzbDrone.Api.Authentication { - public interface IEnableBasicAuthInNancy - { - void Register(IPipelines pipelines); - } - - public class EnableBasicAuthInNancy : IEnableBasicAuthInNancy + public class EnableBasicAuthInNancy : IRegisterNancyPipeline { private readonly IAuthenticationService _authenticationService; @@ -28,7 +25,7 @@ private Response RequiresAuthentication(NancyContext context) { Response response = null; - if (!context.Request.Path.StartsWith("/api/") && + if (!context.Request.IsApiRequest() && context.CurrentUser == null && _authenticationService.Enabled) { diff --git a/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs b/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs index 4ad2e2995..68d737387 100644 --- a/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs +++ b/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs @@ -1,17 +1,15 @@ -using System.Linq; +using System; +using System.Linq; using Nancy; using Nancy.Bootstrapper; +using NzbDrone.Api.Extensions; +using NzbDrone.Api.Extensions.Pipelines; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; namespace NzbDrone.Api.Authentication { - public interface IEnableStatelessAuthInNancy - { - void Register(IPipelines pipelines); - } - - public class EnableStatelessAuthInNancy : IEnableStatelessAuthInNancy + public class EnableStatelessAuthInNancy : IRegisterNancyPipeline { private readonly IConfigFileProvider _configFileProvider; @@ -28,18 +26,16 @@ public void Register(IPipelines pipelines) public Response ValidateApiKey(NancyContext context) { Response response = null; - var apiKey = context.Request.Headers["ApiKey"].FirstOrDefault(); - - if (!RuntimeInfo.IsProduction && - (context.Request.UserHostAddress.Equals("localhost") || - context.Request.UserHostAddress.Equals("127.0.0.1") || - context.Request.UserHostAddress.Equals("::1"))) + + if (!RuntimeInfo.IsProduction && context.Request.IsLocalRequest()) { return response; } + + var apiKey = context.Request.Headers.Authorization; - if (context.Request.Path.StartsWith("/api/") && - (apiKey == null || !apiKey.Equals(_configFileProvider.ApiKey))) + if (context.Request.IsApiRequest() && + (String.IsNullOrWhiteSpace(apiKey) || !apiKey.Equals(_configFileProvider.ApiKey))) { response = new Response { StatusCode = HttpStatusCode.Unauthorized }; } diff --git a/NzbDrone.Api/Extensions/RequestExtensions.cs b/NzbDrone.Api/Extensions/RequestExtensions.cs new file mode 100644 index 000000000..3d0329522 --- /dev/null +++ b/NzbDrone.Api/Extensions/RequestExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Nancy; + +namespace NzbDrone.Api.Extensions +{ + public static class RequestExtensions + { + public static bool IsApiRequest(this Request request) + { + return request.Path.StartsWith("/api/", StringComparison.InvariantCultureIgnoreCase); + } + + public static bool IsSignalRRequest(this Request request) + { + return request.Path.StartsWith("/signalr/", StringComparison.InvariantCultureIgnoreCase); + } + + public static bool IsLocalRequest(this Request request) + { + return (request.UserHostAddress.Equals("localhost") || + request.UserHostAddress.Equals("127.0.0.1") || + request.UserHostAddress.Equals("::1")); + } + } +} diff --git a/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs b/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs index 51f263a8f..ae950aae0 100644 --- a/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs +++ b/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs @@ -38,21 +38,7 @@ public override bool CanHandle(string resourceUrl) public override Response GetResponse(string resourceUrl) { - string content; var response = base.GetResponse(resourceUrl); - var stream = new MemoryStream(); - - response.Contents.Invoke(stream); - stream.Position = 0; - - using (var reader = new StreamReader(stream)) - { - content = reader.ReadToEnd(); - } - - content = content.Replace("API_KEY", _configFileProvider.ApiKey); - - response = new StreamResponse(() => StringToStream(content), response.ContentType); response.Headers["X-UA-Compatible"] = "IE=edge"; return response; @@ -70,6 +56,7 @@ private string GetIndexText() text = text.Replace(".css", ".css?v=" + BuildInfo.Version); text = text.Replace(".js", ".js?v=" + BuildInfo.Version); + text = text.Replace("API_KEY", _configFileProvider.ApiKey); return text; } diff --git a/NzbDrone.Api/NancyBootstrapper.cs b/NzbDrone.Api/NancyBootstrapper.cs index e9464ebd5..bee581921 100644 --- a/NzbDrone.Api/NancyBootstrapper.cs +++ b/NzbDrone.Api/NancyBootstrapper.cs @@ -30,8 +30,6 @@ protected override void ApplicationStartup(TinyIoCContainer container, IPipeline RegisterPipelines(pipelines); container.Resolve().Register(); - container.Resolve().Register(pipelines); - container.Resolve().Register(pipelines); container.Resolve().PublishEvent(new ApplicationStartedEvent()); ApplicationPipelines.OnError.AddItemToEndOfPipeline(container.Resolve().HandleException); diff --git a/UI/Mixins/jquery.ajax.js b/UI/Mixins/jquery.ajax.js index 0f7abf8d5..e05a7d8fb 100644 --- a/UI/Mixins/jquery.ajax.js +++ b/UI/Mixins/jquery.ajax.js @@ -21,11 +21,8 @@ define(function () { delete xhr.data; } if (xhr) { - if (!xhr.headers) { - xhr.headers = {}; - } - - xhr.headers["ApiKey"] = window.NzbDrone.ApiKey; + xhr.headers = xhr.headers || {}; + xhr.headers['Authorization'] = window.NzbDrone.ApiKey; } return original.apply(this, arguments); diff --git a/UI/ServerStatus.js b/UI/ServerStatus.js index 020713f5b..589d45fc6 100644 --- a/UI/ServerStatus.js +++ b/UI/ServerStatus.js @@ -5,7 +5,7 @@ var statusText = $.ajax({ url : window.NzbDrone.ApiRoot + '/system/status', async: false, headers: { - ApiKey: window.NzbDrone.ApiKey + Authorization: window.NzbDrone.ApiKey } }).responseText; diff --git a/UI/index.html b/UI/index.html index a5aa6ba57..c2b0897e7 100644 --- a/UI/index.html +++ b/UI/index.html @@ -62,7 +62,7 @@