This commit is contained in:
KZ 2015-07-20 22:08:05 +01:00
parent 07fe9d1e07
commit 4ec5815897
17 changed files with 430 additions and 52 deletions

View File

@ -53,6 +53,9 @@
<Reference Include="Autofac.Integration.WebApi.Owin">
<HintPath>..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Core">
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
@ -65,10 +68,19 @@
<HintPath>..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Hosting">
<HintPath>..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies">
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.StaticFiles, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
<Private>True</Private>

View File

@ -4,6 +4,7 @@
<package id="Autofac.Owin" version="3.1.0" targetFramework="net452" userInstalled="true" />
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net452" userInstalled="true" />
<package id="Autofac.WebApi2.Owin" version="3.2.0" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" userInstalled="true" />
@ -12,7 +13,10 @@
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Owin.FileSystems" version="3.0.1" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Security.Cookies" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" userInstalled="true" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" userInstalled="true" />
<package id="NLog" version="4.0.1" targetFramework="net452" />

View File

@ -5,15 +5,22 @@ loadJackettSettings();
function loadJackettSettings() {
getJackettConfig(function (data) {
$("#api-key-input").val(data.config.api_key);
$("#app-version").html(data.app_version);
$("#jackett-port").val(data.config.port);
var password = data.config.password;
$("#jackett-adminpwd").val(password);
if (password != null && password != '') {
$("#logoutBtn").show();
}
});
}
$("#change-jackett-port").click(function () {
var jackett_port = $("#jackett-port").val();
var jsonObject = JSON.parse('{"port":"' + jackett_port + '"}');
var jqxhr = $.post("admin/apply_jackett_config", JSON.stringify(jsonObject), function (data) {
var jsonObject = { port: jackett_port};
var jqxhr = $.post("/admin/apply_jackett_config", JSON.stringify(jsonObject), function (data) {
if (data.result == "error") {
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
@ -34,8 +41,30 @@ $("#change-jackett-port").click(function () {
});
});
$("#change-jackett-password").click(function () {
var password = $("#jackett-adminpwd").val();
var jsonObject = { password: password };
var jqxhr = $.post("/admin/set_admin_password", JSON.stringify(jsonObject), function (data) {
if (data.result == "error") {
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
return;
} else {
doNotify("Admin password has been set.", "success", "glyphicon glyphicon-ok");
window.setTimeout(function () {
window.location = window.location.pathname;
}, 1000);
}
}).fail(function () {
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
});
});
function getJackettConfig(callback) {
var jqxhr = $.get("admin/get_jackett_config", function (data) {
var jqxhr = $.get("/admin/get_jackett_config", function (data) {
callback(data);
}).fail(function () {
@ -47,9 +76,7 @@ function reloadIndexers() {
$('#indexers').hide();
$('#indexers > .indexer').remove();
$('#unconfigured-indexers').empty();
var jqxhr = $.get("admin/get_indexers", function (data) {
$("#api-key-input").val(data.api_key);
$("#app-version").html(data.app_version);
var jqxhr = $.get("/admin/get_indexers", function (data) {
displayIndexers(data.items);
}).fail(function () {
doNotify("Error loading indexers, request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
@ -82,7 +109,7 @@ function prepareDeleteButtons() {
var $btn = $(btn);
var id = $btn.data("id");
$btn.click(function () {
var jqxhr = $.post("admin/delete_indexer", JSON.stringify({ indexer: id }), function (data) {
var jqxhr = $.post("/admin/delete_indexer", JSON.stringify({ indexer: id }), function (data) {
if (data.result == "error") {
doNotify("Delete error for " + id + "\n" + data.error, "danger", "glyphicon glyphicon-alert");
}
@ -114,7 +141,7 @@ function prepareTestButtons() {
var id = $btn.data("id");
$btn.click(function () {
doNotify("Test started for " + id, "info", "glyphicon glyphicon-transfer");
var jqxhr = $.post("admin/test_indexer", JSON.stringify({ indexer: id }), function (data) {
var jqxhr = $.post("/admin/test_indexer", JSON.stringify({ indexer: id }), function (data) {
if (data.result == "error") {
doNotify("Test failed for " + data.name + "\n" + data.error, "danger", "glyphicon glyphicon-alert");
}
@ -130,7 +157,7 @@ function prepareTestButtons() {
function displayIndexerSetup(id) {
var jqxhr = $.post("admin/get_config_form", JSON.stringify({ indexer: id }), function (data) {
var jqxhr = $.post("/admin/get_config_form", JSON.stringify({ indexer: id }), function (data) {
if (data.result == "error") {
doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert");
return;
@ -200,7 +227,7 @@ function populateSetupForm(indexerId, name, config) {
$goButton.prop('disabled', true);
$goButton.html($('#templates > .spinner')[0].outerHTML);
var jqxhr = $.post("admin/configure_indexer", JSON.stringify(data), function (data) {
var jqxhr = $.post("/admin/configure_indexer", JSON.stringify(data), function (data) {
if (data.result == "error") {
if (data.config) {
populateConfigItems(configForm, data.config);

View File

@ -6,21 +6,21 @@
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
<script src="jquery-2.1.3.min.js"></script>
<script src="handlebars-v3.0.1.js"></script>
<script src="bootstrap/bootstrap.min.js"></script>
<script src="bootstrap-notify.js"></script>
<script src="/jquery-2.1.3.min.js"></script>
<script src="/handlebars-v3.0.1.js"></script>
<script src="/bootstrap/bootstrap.min.js"></script>
<script src="/bootstrap-notify.js"></script>
<link href="bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="animate.css" rel="stylesheet">
<link href="custom.css" rel="stylesheet">
<link href="/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="/animate.css" rel="stylesheet">
<link href="/custom.css" rel="stylesheet">
<title>Jackett</title>
</head>
<body>
<div id="page">
<img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span>
<img id="logo" src="/jacket_medium.png" /><span id="header-title">Jackett</span>
<hr />
@ -35,6 +35,16 @@
<span class="input-header">Jackett API Key: </span>
<input id="api-key-input" class="form-control input-right" type="text" value="" placeholder="API Key" readonly="">
</div>
<div class="input-area">
<span class="input-header">Admin Password: </span>
<input id="jackett-adminpwd" class="form-control input-right" type="password" value="" placeholder="Blank to disable">
<button id="change-jackett-password" class="btn btn-primary btn-sm">
Set <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
</button>
<a href="Dashboard?logout=true" id="logoutBtn" style="display:none" class="btn btn-danger btn-sm">
Logout
</a>
</div>
<hr />
<div class="input-area">
<span class="input-header">Jackett port: </span>
@ -101,7 +111,7 @@
</button>
<div class="configured-indexer indexer card">
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="/logos/{{id}}.png" /></div>
<div class="indexer-buttons">
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
@ -123,7 +133,7 @@
</div>
<div class="unconfigured-indexer card">
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="/logos/{{id}}.png" /></div>
<div class="indexer-buttons">
<a class="btn btn-info" target="_blank" href="{{site_link}}">Visit <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
@ -153,7 +163,7 @@
</div>
<script src="custom.js"></script>
<script src="/custom.js"></script>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
<script src="/jquery-2.1.3.min.js"></script>
<script src="/handlebars-v3.0.1.js"></script>
<script src="/bootstrap/bootstrap.min.js"></script>
<script src="/bootstrap-notify.js"></script>
<link href="/bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="/animate.css" rel="stylesheet">
<link href="/custom.css" rel="stylesheet">
<title>Jackett</title>
</head>
<body>
<div id="page">
<img id="logo" src="/jacket_medium.png" /><span id="header-title">Jackett</span>
<hr />
<h1>Login</h1>
<form action="/Admin/Dashboard" method="post">
<div class="input-area">
<span class="input-header">Admin password</span>
<input id="password" name="password" class="form-control input-right" type="password">
</div>
<div class="input-area">
<input type="submit" value="Login" />
</div>
</form>
</div>
</body>
</html>

View File

@ -1,30 +1,40 @@
using Autofac;
using Jackett.Models;
using Jackett.Services;
using Jackett.Utils;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Results;
using System.Web.Security;
namespace Jackett.Controllers
{
[RoutePrefix("admin")]
[JackettAuthorized]
public class AdminController : ApiController
{
private IConfigurationService config;
private IIndexerManagerService indexerService;
private IServerService serverService;
private ISecuityService securityService;
public AdminController(IConfigurationService config, IIndexerManagerService i, IServerService ss)
public AdminController(IConfigurationService config, IIndexerManagerService i, IServerService ss, ISecuityService s)
{
this.config = config;
indexerService = i;
serverService = ss;
securityService = s;
}
private async Task<JToken> ReadPostDataJson()
@ -33,6 +43,91 @@ namespace Jackett.Controllers
return JObject.Parse(content);
}
private HttpResponseMessage GetFile(string path)
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
var mappedPath = Path.Combine(config.GetContentFolder(), path);
var stream = new FileStream(mappedPath, FileMode.Open);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(mappedPath));
return result;
}
[HttpGet]
[AllowAnonymous]
public RedirectResult Logout()
{
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignOut("ApplicationCookie");
return Redirect("/Admin/Dashboard");
}
[HttpGet]
[HttpPost]
[AllowAnonymous]
public async Task<HttpResponseMessage> Dashboard()
{
if(Request.RequestUri.Query!=null && Request.RequestUri.Query.Contains("logout"))
{
var file = GetFile("login.html");
securityService.Logout(file);
return file;
}
if (securityService.CheckAuthorised(Request))
{
return GetFile("index.html");
} else
{
var formData = await Request.Content.ReadAsFormDataAsync();
if (formData!=null && securityService.HashPassword(formData["password"]) == serverService.Config.AdminPassword)
{
var file = GetFile("index.html");
securityService.Login(file);
return file;
} else
{
return GetFile("login.html");
}
}
}
[Route("set_admin_password")]
[HttpPost]
public async Task<IHttpActionResult> SetAdminPassword()
{
var jsonReply = new JObject();
try
{
var postData = await ReadPostDataJson();
var password = (string)postData["password"];
if (string.IsNullOrEmpty(password))
{
serverService.Config.AdminPassword = string.Empty;
}
else
{
serverService.Config.AdminPassword = securityService.HashPassword(password);
}
serverService.SaveConfig();
jsonReply["result"] = "success";
}
catch (Exception ex)
{
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
return Json(jsonReply);
}
[Route("get_config_form")]
[HttpPost]
public async Task<IHttpActionResult> GetConfigForm()
@ -92,8 +187,6 @@ namespace Jackett.Controllers
try
{
jsonReply["result"] = "success";
jsonReply["api_key"] = serverService.Config.APIKey;
jsonReply["app_version"] = config.GetVersion();
JArray items = new JArray();
foreach (var indexer in indexerService.GetAllIndexers())
@ -163,7 +256,13 @@ namespace Jackett.Controllers
var jsonReply = new JObject();
try
{
jsonReply["config"] = config.ReadServerSettingsFile();
var cfg = new JObject();
cfg["port"] = serverService.Config.Port;
cfg["api_key"] = serverService.Config.APIKey;
cfg["password"] = string.IsNullOrEmpty(serverService.Config.AdminPassword )? string.Empty:serverService.Config.AdminPassword.Substring(0,10);
jsonReply["config"] = cfg;
jsonReply["app_version"] = config.GetVersion();
jsonReply["result"] = "success";
}
catch (CustomException ex)

View File

@ -94,6 +94,15 @@ namespace Jackett
}
}
public static ISecuityService SecurityService
{
get
{
return container.Resolve<ISecuityService>();
}
}
private static void SetupLogging(ContainerBuilder builder)
{
var logConfig = new LoggingConfiguration();

View File

@ -73,6 +73,9 @@
<Reference Include="CsQuery">
<HintPath>..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Core">
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
@ -85,10 +88,19 @@
<HintPath>..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Hosting, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies">
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.StaticFiles, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
<Private>True</Private>
@ -146,6 +158,7 @@
<Compile Include="Indexers\SpeedCD.cs" />
<Compile Include="Models\Config\ServerConfig.cs" />
<Compile Include="Services\ProcessService.cs" />
<Compile Include="Services\SecuityService.cs" />
<Compile Include="Services\SerializeService.cs" />
<Compile Include="Services\ServiceConfigService.cs" />
<Compile Include="Services\SpinService.cs" />
@ -184,6 +197,7 @@
<Compile Include="Indexers\Torrentz.cs" />
<Compile Include="JackettModule.cs" />
<Compile Include="Utils\DateTimeUtil.cs" />
<Compile Include="Utils\JackettAuthorizedAttribute.cs" />
<Compile Include="Utils\ParseUtil.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
@ -202,6 +216,7 @@
<Compile Include="CurlHelper.cs" />
<Compile Include="Indexers\AlphaRatio.cs" />
<Compile Include="Utils\StringUtil.cs" />
<Compile Include="Utils\WebApiRootRedirectMiddleware.cs" />
<Compile Include="Utils\WebAPIRequestLogger.cs" />
<Compile Include="Utils\WebAPIToNLogTracer.cs" />
</ItemGroup>
@ -240,6 +255,9 @@
<Content Include="Content\fonts\glyphicons-halflings-regular.svg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\login.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Content\logos\animebytes.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@ -17,6 +17,7 @@ namespace Jackett.Models.Config
public int Port { get; set; }
public bool AllowExternal { get; set; }
public string APIKey { get; set; }
public string AdminPassword { get; set; }
public string GetListenAddress(bool? external = null)
{

View File

@ -18,7 +18,6 @@ namespace Jackett.Services
string GetVersion();
string GetIndexerConfigDir();
string GetAppDataFolder();
JObject ReadServerSettingsFile();
string GetSonarrConfigFile();
T GetConfig<T>();
void SaveConfig<T>(T config);
@ -168,24 +167,5 @@ namespace Jackett.Services
{
return Path.Combine(GetAppDataFolder(), "sonarr_api.json");
}
public JObject ReadServerSettingsFile()
{
var path = GetConfigFile();
JObject jsonReply = new JObject();
if (File.Exists(path))
{
jsonReply = JObject.Parse(File.ReadAllText(path));
// Port = (int)jsonReply["port"];
// ListenPublic = (bool)jsonReply["public"];
}
else
{
// jsonReply["port"] = Port;
// jsonReply["public"] = ListenPublic;
}
return jsonReply;
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Jackett.Services
{
public interface ISecuityService
{
bool CheckAuthorised(HttpRequestMessage request);
string HashPassword(string input);
void Login(HttpResponseMessage request);
void Logout(HttpResponseMessage request);
}
class SecuityService : ISecuityService
{
private const string COOKIENAME = "JACKETT";
private IServerService serverService;
public SecuityService(IServerService ss)
{
serverService = ss;
}
public string HashPassword(string input)
{
// Append key as salt
input += serverService.Config.APIKey;
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(input);
SHA512Managed hashString = new SHA512Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
public void Login(HttpResponseMessage response)
{
// Login
response.Headers.Add("Set-Cookie", COOKIENAME + "=" + serverService.Config.AdminPassword + "; path=/");
}
public void Logout(HttpResponseMessage response)
{
// Logout
response.Headers.Add("Set-Cookie", COOKIENAME + "=; path=/");
}
public bool CheckAuthorised(HttpRequestMessage request)
{
if (string.IsNullOrEmpty(Engine.Server.Config.AdminPassword))
return true;
try
{
var cookie = request.Headers.GetCookies(COOKIENAME).FirstOrDefault();
if (cookie != null)
{
return cookie[COOKIENAME].Value == serverService.Config.AdminPassword;
}
}
catch { }
return false;
}
}
}

View File

@ -26,6 +26,7 @@ namespace Jackett.Services
void Stop();
void ReserveUrls(bool doInstall = true);
ServerConfig Config { get; }
void SaveConfig();
}
public class ServerService : IServerService
@ -94,6 +95,11 @@ namespace Jackett.Services
}
}
public void SaveConfig()
{
configService.SaveConfig<ServerConfig>(config);
}
public void Start()
{
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");

View File

@ -15,6 +15,8 @@ using Autofac;
using Jackett.Services;
using System.Web.Http.Tracing;
using Jackett.Utils;
using Microsoft.Owin.Security.Cookies;
using Microsoft.AspNet.Identity;
[assembly: OwinStartup(typeof(Startup))]
namespace Jackett
@ -25,6 +27,9 @@ namespace Jackett
{
// Configure Web API for self-host.
var config = new HttpConfiguration();
appBuilder.Use<WebApiRootRedirectMiddleware>();
// Setup tracing if enabled
if (Engine.TracingEnabled)
{
@ -63,11 +68,18 @@ namespace Jackett
defaults: new { controller = "Download", action = "Download" }
);
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Admin/Login")
});
appBuilder.UseFileServer(new FileServerOptions
{
RequestPath = new PathString(string.Empty),
FileSystem = new PhysicalFileSystem(Engine.ConfigService.GetContentFolder()),
EnableDirectoryBrowsing = true,
EnableDirectoryBrowsing = false,
});
appBuilder.UseWebApi(config);

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Jackett.Utils
{
public class JackettAuthorizedAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
// Skip authorisation on blank passwords
if (string.IsNullOrEmpty(Engine.Server.Config.AdminPassword))
{
return;
}
if (!Engine.SecurityService.CheckAuthorised(actionContext.Request))
{
if(actionContext.ControllerContext.ControllerDescriptor.ControllerType.GetCustomAttributes(true).Where(a => a.GetType() == typeof(AllowAnonymousAttribute)).Any())
{
return;
}
if (actionContext.ControllerContext.ControllerDescriptor.ControllerType.GetMethod(actionContext.ActionDescriptor.ActionName).GetCustomAttributes(true).Where(a => a.GetType() == typeof(AllowAnonymousAttribute)).Any())
{
return;
}
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode
.Unauthorized);
}
}
}
}

View File

@ -24,11 +24,13 @@ namespace Jackett.Utils
return await base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
//once response is ready, log it
var responseBody = task.Result.Content.ReadAsStringAsync().Result;
Trace.WriteLine(responseBody);
Engine.Logger.Debug("Response: " + responseBody);
if (null != task.Result.Content)
{
//once response is ready, log it
var responseBody = task.Result.Content.ReadAsStringAsync().Result;
Trace.WriteLine(responseBody);
Engine.Logger.Debug("Response: " + responseBody);
}
return task.Result;
});
}

View File

@ -0,0 +1,32 @@
using Microsoft.Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Jackett.Utils
{
public class WebApiRootRedirectMiddleware : OwinMiddleware
{
public WebApiRootRedirectMiddleware(OwinMiddleware next)
: base(next)
{
}
public async override Task Invoke(IOwinContext context)
{
var url = context.Request.Uri;
if (string.IsNullOrWhiteSpace(url.AbsolutePath) || url.AbsolutePath == "/")
{
// 301 is the status code of permanent redirect
context.Response.StatusCode = 301;
context.Response.Headers.Set("Location", "/Admin/Dashboard");
}
else
{
await Next.Invoke(context);
}
}
}
}

View File

@ -6,6 +6,7 @@
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net451" userInstalled="true" />
<package id="Autofac.WebApi2.Owin" version="3.2.0" targetFramework="net451" userInstalled="true" />
<package id="CsQuery" version="1.3.4" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net451" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net451" userInstalled="true" />
@ -14,7 +15,10 @@
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.Owin.FileSystems" version="3.0.1" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net451" />
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net451" userInstalled="true" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net451" />
<package id="Microsoft.Owin.Security.Cookies" version="3.0.1" targetFramework="net451" />
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net451" userInstalled="true" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net451" userInstalled="true" />
<package id="NLog" version="4.0.1" targetFramework="net451" userInstalled="true" />