mirror of https://github.com/Jackett/Jackett
Web auth
This commit is contained in:
parent
07fe9d1e07
commit
4ec5815897
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
|
Loading…
Reference in New Issue