commit
53f4966e97
@ -0,0 +1,62 @@
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Basic;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Bootstrapper;
|
||||
using Nancy.Cryptography;
|
||||
using NzbDrone.Api.Extensions.Pipelines;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Authentication
|
||||
{
|
||||
public class EnableAuthInNancy : IRegisterNancyPipeline
|
||||
{
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public EnableAuthInNancy(IAuthenticationService authenticationService,
|
||||
IConfigService configService,
|
||||
IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_authenticationService = authenticationService;
|
||||
_configService = configService;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
RegisterFormsAuth(pipelines);
|
||||
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, "Sonarr"));
|
||||
pipelines.BeforeRequest.AddItemToEndOfPipeline(RequiresAuthentication);
|
||||
}
|
||||
|
||||
private Response RequiresAuthentication(NancyContext context)
|
||||
{
|
||||
Response response = null;
|
||||
|
||||
if (!_authenticationService.IsAuthenticated(context))
|
||||
{
|
||||
response = new Response { StatusCode = HttpStatusCode.Unauthorized };
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void RegisterFormsAuth(IPipelines pipelines)
|
||||
{
|
||||
var cryptographyConfiguration = new CryptographyConfiguration(
|
||||
new RijndaelEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase,
|
||||
new byte[] {1, 2, 3, 4, 5, 6, 7, 8})),
|
||||
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase,
|
||||
new byte[] {1, 2, 3, 4, 5, 6, 7, 8}))
|
||||
);
|
||||
|
||||
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
|
||||
{
|
||||
RedirectUrl = "~/login",
|
||||
UserMapper = _authenticationService,
|
||||
CryptographyConfiguration = cryptographyConfiguration
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Nancy;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Frontend.Mappers
|
||||
{
|
||||
public class LoginHtmlMapper : StaticResourceMapperBase
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
|
||||
private readonly string _indexPath;
|
||||
private static readonly Regex ReplaceRegex = new Regex("(?<=(?:href|src|data-main)=\").*?(?=\")", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static String URL_BASE;
|
||||
private string _generatedContent;
|
||||
|
||||
public LoginHtmlMapper(IAppFolderInfo appFolderInfo,
|
||||
IDiskProvider diskProvider,
|
||||
IConfigFileProvider configFileProvider,
|
||||
Func<ICacheBreakerProvider> cacheBreakProviderFactory,
|
||||
Logger logger)
|
||||
: base(diskProvider, logger)
|
||||
{
|
||||
_diskProvider = diskProvider;
|
||||
_cacheBreakProviderFactory = cacheBreakProviderFactory;
|
||||
_indexPath = Path.Combine(appFolderInfo.StartUpFolder, "UI", "login.html");
|
||||
|
||||
URL_BASE = configFileProvider.UrlBase;
|
||||
}
|
||||
|
||||
public override string Map(string resourceUrl)
|
||||
{
|
||||
return _indexPath;
|
||||
}
|
||||
|
||||
public override bool CanHandle(string resourceUrl)
|
||||
{
|
||||
return resourceUrl.StartsWith("/login");
|
||||
}
|
||||
|
||||
public override Response GetResponse(string resourceUrl)
|
||||
{
|
||||
var response = base.GetResponse(resourceUrl);
|
||||
response.Headers["X-UA-Compatible"] = "IE=edge";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
protected override Stream GetContentStream(string filePath)
|
||||
{
|
||||
var text = GetLoginText();
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.Write(text);
|
||||
writer.Flush();
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
|
||||
private string GetLoginText()
|
||||
{
|
||||
if (RuntimeInfoBase.IsProduction && _generatedContent != null)
|
||||
{
|
||||
return _generatedContent;
|
||||
}
|
||||
|
||||
var text = _diskProvider.ReadAllText(_indexPath);
|
||||
|
||||
var cacheBreakProvider = _cacheBreakProviderFactory();
|
||||
|
||||
text = ReplaceRegex.Replace(text, match =>
|
||||
{
|
||||
var url = cacheBreakProvider.AddCacheBreakerToPath(match.Value);
|
||||
return URL_BASE + url;
|
||||
});
|
||||
|
||||
_generatedContent = text;
|
||||
|
||||
return _generatedContent;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head runat="server">
|
||||
<title>Sonarr - Login</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<link href="/Content/bootstrap.css" rel='stylesheet' type='text/css'/>
|
||||
<link href="/Content/theme.css" rel='stylesheet' type='text/css'/>
|
||||
|
||||
<link rel="apple-touch-icon" href="/Content/Images/touch/57.png"/>
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/Content/Images/touch/72.png"/>
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/Content/Images/touch/114.png"/>
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/Content/Images/touch/144.png"/>
|
||||
<link rel="icon" type="image/ico" href="/Content/Images/favicon.ico"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div id="nav-region"></div>
|
||||
</div>
|
||||
<div id="page">
|
||||
<div class="page-container">
|
||||
<div class="container-fluid">
|
||||
<div class="container-fluid main-region" id="main-region">
|
||||
<div class="col-md-2 col-md-offset-5">
|
||||
<form name="login" id="login" class="login" method="POST">
|
||||
<h2><img src="/Content/Images/logos/32.png" alt=""/> Sonarr</h2>
|
||||
<div class="form-group">
|
||||
<label for="username" class="sr-only">Email address</label>
|
||||
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="sr-only">Password</label>
|
||||
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="rememberMe" checked="checked"> Remember me
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input class="btn btn-lg btn-primary btn-block" type="submit" value="Log in" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-region"></div>
|
||||
<div id="file-browser-modal-region"></div>
|
||||
</div>
|
||||
</div>
|
||||
<a id="scroll-up" title="Back to the top!">
|
||||
<i class="icon-circle-arrow-up"></i>
|
||||
</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in new issue