mirror of https://github.com/Jackett/Jackett
Saved password security
This commit is contained in:
parent
1725550b21
commit
6d0aa05761
|
@ -54,6 +54,10 @@
|
|||
<HintPath>..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="AutoMapper, Version=4.0.4.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.4.0.4\lib\net45\AutoMapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="CsQuery, Version=1.3.3.249, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -70,6 +74,7 @@
|
|||
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<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>
|
||||
|
@ -155,6 +160,7 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="Indexers\BakaBTTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\ProtectionServiceTests.cs" />
|
||||
<Compile Include="TestBase.cs" />
|
||||
<Compile Include="TestIIndexerManagerServiceHelper.cs" />
|
||||
<Compile Include="TestUtil.cs" />
|
||||
|
@ -162,7 +168,9 @@
|
|||
<Compile Include="Util\ServerUtilTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="app.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using Jackett.Services;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac;
|
||||
|
||||
namespace JackettTest.Services
|
||||
{
|
||||
[TestFixture]
|
||||
class ProtectionServiceTests : TestBase
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Should_be_able_to_encrypt_and_decrypt()
|
||||
{
|
||||
var ss = TestUtil.Container.Resolve<IServerService>();
|
||||
ss.Config.InstanceId = "12345678";
|
||||
var ps = TestUtil.Container.Resolve<IProtectionService>();
|
||||
var input = "test123";
|
||||
var protectedInput = ps.Protect(input);
|
||||
var output = ps.UnProtect(protectedInput);
|
||||
|
||||
Assert.AreEqual(output, input);
|
||||
Assert.AreNotEqual(input, protectedInput);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,10 @@
|
|||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup></configuration>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<package id="Autofac.Owin" version="3.1.0" targetFramework="net45" />
|
||||
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net45" />
|
||||
<package id="Autofac.WebApi2.Owin" version="3.2.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="4.0.4" targetFramework="net45" />
|
||||
<package id="CsQuery" version="1.3.4" targetFramework="net45" />
|
||||
<package id="FluentAssertions" version="3.4.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
|
||||
|
|
|
@ -153,7 +153,7 @@ namespace Jackett.Controllers
|
|||
var postData = await ReadPostDataJson();
|
||||
var indexer = indexerService.GetIndexer((string)postData["indexer"]);
|
||||
var config = await indexer.GetConfigurationForSetup();
|
||||
jsonReply["config"] = config.ToJson();
|
||||
jsonReply["config"] = config.ToJson(null);
|
||||
jsonReply["caps"] = indexer.TorznabCaps.CapsToJson();
|
||||
jsonReply["name"] = indexer.DisplayName;
|
||||
jsonReply["result"] = "success";
|
||||
|
@ -192,7 +192,7 @@ namespace Jackett.Controllers
|
|||
baseIndexer.ResetBaseConfig();
|
||||
if (ex is ExceptionWithConfigData)
|
||||
{
|
||||
jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson();
|
||||
jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson(null);
|
||||
} else
|
||||
{
|
||||
logger.Error(ex, "Exception in Configure");
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace Jackett.Indexers
|
|||
private string DownloadUrl { get { return SiteLink + "torrents.php?action=download&id="; } }
|
||||
private string GuidUrl { get { return SiteLink + "torrents.php?torrentid="; } }
|
||||
|
||||
public AlphaRatio(IIndexerManagerService i, IWebClient w, Logger l)
|
||||
public AlphaRatio(IIndexerManagerService i, IWebClient w, Logger l, IProtectionService ps)
|
||||
: base(name: "AlphaRatio",
|
||||
description: "Legendary",
|
||||
link: "https://alpharatio.cc/",
|
||||
|
@ -33,6 +33,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
webclient = w;
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public AnimeBytes(IIndexerManagerService i, IWebClient client, Logger l)
|
||||
public AnimeBytes(IIndexerManagerService i, IWebClient client, Logger l, IProtectionService ps)
|
||||
: base(name: "AnimeBytes",
|
||||
link: "https://animebytes.tv/",
|
||||
description: "Powered by Tentacles",
|
||||
|
@ -41,6 +41,7 @@ namespace Jackett.Indexers
|
|||
client: client,
|
||||
caps: new TorznabCapabilities(TorznabCatType.Anime),
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataAnimeBytes())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BB(IIndexerManagerService i, Logger l, IWebClient w)
|
||||
public BB(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "bB",
|
||||
description: "bB",
|
||||
link: "http://www.reddit.com/r/baconbits/",
|
||||
|
@ -40,6 +40,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public BakaBT(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "BakaBT",
|
||||
description: "Anime Community",
|
||||
link: "http://bakabt.me/",
|
||||
|
@ -36,6 +36,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace Jackett.Indexers
|
|||
protected static List<CachedQueryResult> cache = new List<CachedQueryResult>();
|
||||
protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0);
|
||||
protected IWebClient webclient;
|
||||
protected IProtectionService protectionService;
|
||||
|
||||
protected string CookieHeader
|
||||
{
|
||||
get { return configData.CookieHeader.Value; }
|
||||
|
@ -39,7 +41,7 @@ namespace Jackett.Indexers
|
|||
|
||||
private List<CategoryMapping> categoryMapping = new List<CategoryMapping>();
|
||||
|
||||
public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, TorznabCapabilities caps = null)
|
||||
public BaseIndexer(string name, string link, string description, IIndexerManagerService manager, IWebClient client, Logger logger, ConfigurationData configData, IProtectionService p, TorznabCapabilities caps = null)
|
||||
{
|
||||
if (!link.EndsWith("/"))
|
||||
throw new Exception("Site link must end with a slash.");
|
||||
|
@ -50,6 +52,7 @@ namespace Jackett.Indexers
|
|||
this.logger = logger;
|
||||
indexerService = manager;
|
||||
webclient = client;
|
||||
protectionService = p;
|
||||
|
||||
this.configData = configData;
|
||||
|
||||
|
@ -91,7 +94,7 @@ namespace Jackett.Indexers
|
|||
|
||||
protected virtual void SaveConfig()
|
||||
{
|
||||
indexerService.SaveConfig(this as IIndexer, configData.ToJson(forDisplay: false));
|
||||
indexerService.SaveConfig(this as IIndexer, configData.ToJson(protectionService,forDisplay: false));
|
||||
}
|
||||
|
||||
protected void OnParseError(string results, Exception ex)
|
||||
|
@ -182,7 +185,7 @@ namespace Jackett.Indexers
|
|||
{
|
||||
if (jsonConfig is JArray)
|
||||
{
|
||||
configData.LoadValuesFromJson(jsonConfig);
|
||||
configData.LoadValuesFromJson(jsonConfig, protectionService);
|
||||
IsConfigured = true;
|
||||
}
|
||||
// read and upgrade old settings file format
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BeyondHD(IIndexerManagerService i, Logger l, IWebClient w)
|
||||
public BeyondHD(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "BeyondHD",
|
||||
description: "Without BeyondHD, your HDTV is just a TV",
|
||||
link: "https://beyondhd.me/",
|
||||
|
@ -36,6 +36,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCookie())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BitHdtv(IIndexerManagerService i, Logger l, IWebClient w)
|
||||
public BitHdtv(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "BIT-HDTV",
|
||||
description: "Home of high definition invites",
|
||||
link: "https://www.bit-hdtv.com/",
|
||||
|
@ -38,6 +38,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public BitMeTV(IIndexerManagerService i, Logger l, IWebClient c)
|
||||
public BitMeTV(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps)
|
||||
: base(name: "BitMeTV",
|
||||
description: "TV Episode specialty tracker",
|
||||
link: "http://www.bitmetv.org/",
|
||||
|
@ -40,6 +40,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCaptchaLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Demonoid(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public Demonoid(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Demonoid",
|
||||
description: "Demonoid",
|
||||
link: "http://www.demonoid.pw/",
|
||||
|
@ -35,6 +35,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public FrenchTorrentDb(IIndexerManagerService i, Logger l, IWebClient c)
|
||||
public FrenchTorrentDb(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps)
|
||||
: base(name: "FrenchTorrentDb",
|
||||
description: "One the biggest French Torrent Tracker",
|
||||
link: "http://www.frenchtorrentdb.com/",
|
||||
|
@ -32,6 +32,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataCookie())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Freshon(IIndexerManagerService i, Logger l, IWebClient c)
|
||||
public Freshon(IIndexerManagerService i, Logger l, IWebClient c, IProtectionService ps)
|
||||
: base(name: "FreshOnTV",
|
||||
description: "Our goal is to provide the latest stuff in the TV show domain",
|
||||
link: "https://freshon.tv/",
|
||||
|
@ -41,6 +41,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public HDSpace(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public HDSpace(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "HD-Space",
|
||||
description: "Sharing The Universe",
|
||||
link: "https://hd-space.org/",
|
||||
|
@ -36,6 +36,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public HDTorrents(IIndexerManagerService i, Logger l, IWebClient w)
|
||||
public HDTorrents(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "HD-Torrents",
|
||||
description: "HD-Torrents is a private torrent website with HD torrents and strict rules on their content.",
|
||||
link: "http://hdts.ru/",// Of the accessible domains the .ru seems the most reliable. https://hdts.ru | https://hd-torrents.org | https://hd-torrents.net | https://hd-torrents.me
|
||||
|
@ -38,6 +38,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public IPTorrents(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public IPTorrents(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "IPTorrents",
|
||||
description: "Always a step ahead.",
|
||||
link: "https://iptorrents.com/",
|
||||
|
@ -37,6 +37,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(72, TorznabCatType.Movies);
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public ImmortalSeed(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public ImmortalSeed(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "ImmortalSeed",
|
||||
description: "ImmortalSeed",
|
||||
link: "http://immortalseed.me/",
|
||||
|
@ -38,6 +38,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(32, TorznabCatType.Anime);
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public MoreThanTV(IIndexerManagerService i, IWebClient c, Logger l)
|
||||
public MoreThanTV(IIndexerManagerService i, IWebClient c, Logger l, IProtectionService ps)
|
||||
: base(name: "MoreThanTV",
|
||||
description: "ROMANIAN Private Torrent Tracker for TV / MOVIES, and the internal tracker for the release group DRACULA.",
|
||||
link: "https://www.morethan.tv/",
|
||||
|
@ -39,6 +39,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public Pretome(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public Pretome(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "PreToMe",
|
||||
description: "BitTorrent site for High Quality, High Definition (HD) movies and TV Shows",
|
||||
link: "https://pretome.info/",
|
||||
|
@ -35,6 +35,7 @@ namespace Jackett.Indexers
|
|||
client: wc,
|
||||
manager: i,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataPinNumber())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public PrivateHD(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public PrivateHD(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "PrivateHD",
|
||||
description: "BitTorrent site for High Quality, High Definition (HD) movies and TV Shows",
|
||||
link: "https://privatehd.to/",
|
||||
|
@ -37,6 +37,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
AddCategoryMapping(1, TorznabCatType.Movies);
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public RuTor(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public RuTor(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "RUTor",
|
||||
description: "Свободный торрент трекер",
|
||||
link: "http://rutor.org/",
|
||||
|
@ -40,6 +40,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataRuTor(defaultSiteLink))
|
||||
{
|
||||
TorznabCaps.Categories.Add(TorznabCatType.Anime);
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public SceneAccess(IIndexerManagerService i, IWebClient c, Logger l)
|
||||
public SceneAccess(IIndexerManagerService i, IWebClient c, Logger l, IProtectionService ps)
|
||||
: base(name: "SceneAccess",
|
||||
description: "Your gateway to the scene",
|
||||
link: "https://sceneaccess.eu/",
|
||||
|
@ -35,6 +35,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: c,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public SceneTime(IIndexerManagerService i, Logger l, IWebClient w)
|
||||
public SceneTime(IIndexerManagerService i, Logger l, IWebClient w, IProtectionService ps)
|
||||
: base(name: "SceneTime",
|
||||
description: "Always on time",
|
||||
link: "https://www.scenetime.com/",
|
||||
|
@ -38,6 +38,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: w,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public ShowRSS(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public ShowRSS(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "ShowRSS",
|
||||
description: "showRSS is a service that allows you to keep track of your favorite TV shows",
|
||||
link: defaultSiteLink,
|
||||
|
@ -44,6 +44,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public SpeedCD(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public SpeedCD(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Speed.cd",
|
||||
description: "Your home now!",
|
||||
link: "http://speed.cd/",
|
||||
|
@ -41,6 +41,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Jackett.Indexers
|
|||
}
|
||||
|
||||
|
||||
public Strike(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public Strike(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Strike",
|
||||
description: "Torrent search engine",
|
||||
link: defaultSiteLink,
|
||||
|
@ -45,6 +45,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public T411(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public T411(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "T411",
|
||||
description: "French Torrent Tracker",
|
||||
link: "http://www.t411.io/",
|
||||
|
@ -43,6 +43,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataLoginTokin())
|
||||
{
|
||||
CommentsUrl = SiteLink + "/torrents/{0}";
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public ThePirateBay(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public ThePirateBay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "The Pirate Bay",
|
||||
description: "The worlds largest bittorrent indexer",
|
||||
link: defaultSiteLink,
|
||||
|
@ -45,6 +45,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentBytes(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public TorrentBytes(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "TorrentBytes",
|
||||
description: "A decade of torrentbytes",
|
||||
link: "https://www.torrentbytes.net/",
|
||||
|
@ -38,6 +38,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public TorrentDay(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "TorrentDay",
|
||||
description: "TorrentDay",
|
||||
link: "https://torrentday.eu/",
|
||||
|
@ -39,6 +39,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentLeech(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public TorrentLeech(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "TorrentLeech",
|
||||
description: "This is what happens when you seed",
|
||||
link: "http://www.torrentleech.org/",
|
||||
|
@ -37,6 +37,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public TorrentShack(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public TorrentShack(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "TorrentShack",
|
||||
description: "TorrentShack",
|
||||
link: "http://torrentshack.me/",
|
||||
|
@ -37,6 +37,7 @@ namespace Jackett.Indexers
|
|||
client: wc,
|
||||
manager: i,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataBasicLogin())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Jackett.Indexers
|
|||
}
|
||||
|
||||
|
||||
public Torrentz(IIndexerManagerService i, Logger l, IWebClient wc)
|
||||
public Torrentz(IIndexerManagerService i, Logger l, IWebClient wc, IProtectionService ps)
|
||||
: base(name: "Torrentz",
|
||||
description: "Torrentz is a meta-search engine and a Multisearch. This means we just search other search engines.",
|
||||
link: defaultSiteLink,
|
||||
|
@ -45,6 +45,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataUrl(defaultSiteLink))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Jackett.Indexers
|
|||
set { base.configData = value; }
|
||||
}
|
||||
|
||||
public NCore(IIndexerManagerService i, IWebClient wc, Logger l)
|
||||
public NCore(IIndexerManagerService i, IWebClient wc, Logger l, IProtectionService ps)
|
||||
: base(name: "nCore",
|
||||
description: "A Hungarian private torrent site.",
|
||||
link: "https://ncore.cc/",
|
||||
|
@ -38,6 +38,7 @@ namespace Jackett.Indexers
|
|||
manager: i,
|
||||
client: wc,
|
||||
logger: l,
|
||||
p: ps,
|
||||
configData: new ConfigurationDataNCore())
|
||||
{
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@
|
|||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
|
@ -207,6 +208,7 @@
|
|||
<Compile Include="Indexers\TorrentLeech.cs" />
|
||||
<Compile Include="Indexers\TorrentShack.cs" />
|
||||
<Compile Include="Indexers\Torrentz.cs" />
|
||||
<Compile Include="JackettProtectedAttribute.cs" />
|
||||
<Compile Include="Models\CachedLog.cs" />
|
||||
<Compile Include="Models\CachedResult.cs" />
|
||||
<Compile Include="Models\CategoryMapping.cs" />
|
||||
|
@ -228,6 +230,7 @@
|
|||
<Compile Include="Models\TrackerCacheResult.cs" />
|
||||
<Compile Include="Services\CacheService.cs" />
|
||||
<Compile Include="Services\LogCacheService.cs" />
|
||||
<Compile Include="Services\ProtectionService.cs" />
|
||||
<Compile Include="Utils\Clients\BaseWebResult.cs" />
|
||||
<Compile Include="Utils\Clients\UnixLibCurlWebClient.cs" />
|
||||
<Compile Include="Utils\Clients\WebByteResult.cs" />
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jackett
|
||||
{
|
||||
public class JackettProtectedAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ namespace Jackett.Models.Config
|
|||
public bool AllowExternal { get; set; }
|
||||
public string APIKey { get; set; }
|
||||
public string AdminPassword { get; set; }
|
||||
public string InstanceId { get; set; }
|
||||
|
||||
public string[] GetListenAddresses(bool? external = null)
|
||||
{
|
||||
|
@ -38,19 +39,5 @@ namespace Jackett.Models.Config
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
public string GenerateApi()
|
||||
{
|
||||
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var randBytes = new byte[32];
|
||||
var rngCsp = new RNGCryptoServiceProvider();
|
||||
rngCsp.GetBytes(randBytes);
|
||||
var key = "";
|
||||
foreach (var b in randBytes)
|
||||
{
|
||||
key += chars[b % chars.Length];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Jackett.Utils;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -11,6 +12,8 @@ namespace Jackett.Models.IndexerConfig
|
|||
{
|
||||
public abstract class ConfigurationData
|
||||
{
|
||||
const string PASSWORD_REPLACEMENT = "|||%%PREVJACKPASSWD%%|||";
|
||||
|
||||
public enum ItemType
|
||||
{
|
||||
InputString,
|
||||
|
@ -27,12 +30,12 @@ namespace Jackett.Models.IndexerConfig
|
|||
|
||||
}
|
||||
|
||||
public ConfigurationData(JToken json)
|
||||
public ConfigurationData(JToken json, IProtectionService ps)
|
||||
{
|
||||
LoadValuesFromJson(json);
|
||||
LoadValuesFromJson(json, ps);
|
||||
}
|
||||
|
||||
public void LoadValuesFromJson(JToken json)
|
||||
public void LoadValuesFromJson(JToken json, IProtectionService ps= null)
|
||||
{
|
||||
var arr = (JArray)json;
|
||||
foreach (var item in GetItems(forDisplay: false))
|
||||
|
@ -44,7 +47,21 @@ namespace Jackett.Models.IndexerConfig
|
|||
switch (item.ItemType)
|
||||
{
|
||||
case ItemType.InputString:
|
||||
((StringItem)item).Value = arrItem.Value<string>("value");
|
||||
var sItem = (StringItem)item;
|
||||
var newValue = arrItem.Value<string>("value");
|
||||
|
||||
if (string.Equals(item.Name, "password", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (newValue != PASSWORD_REPLACEMENT)
|
||||
{
|
||||
sItem.Value = newValue;
|
||||
if (ps != null)
|
||||
sItem.Value = ps.UnProtect(newValue);
|
||||
}
|
||||
} else
|
||||
{
|
||||
sItem.Value = newValue;
|
||||
}
|
||||
break;
|
||||
case ItemType.HiddenData:
|
||||
((HiddenItem)item).Value = arrItem.Value<string>("value");
|
||||
|
@ -56,7 +73,7 @@ namespace Jackett.Models.IndexerConfig
|
|||
}
|
||||
}
|
||||
|
||||
public JToken ToJson(bool forDisplay = true)
|
||||
public JToken ToJson(IProtectionService ps, bool forDisplay = true)
|
||||
{
|
||||
var items = GetItems(forDisplay);
|
||||
var jArray = new JArray();
|
||||
|
@ -71,7 +88,18 @@ namespace Jackett.Models.IndexerConfig
|
|||
case ItemType.InputString:
|
||||
case ItemType.HiddenData:
|
||||
case ItemType.DisplayInfo:
|
||||
jObject["value"] = ((StringItem)item).Value;
|
||||
var value = ((StringItem)item).Value;
|
||||
if (string.Equals(item.Name, "password", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
value = string.Empty;
|
||||
else if (forDisplay)
|
||||
value = PASSWORD_REPLACEMENT;
|
||||
else if (ps != null)
|
||||
value = ps.Protect(value);
|
||||
}
|
||||
|
||||
jObject["value"] = value;
|
||||
break;
|
||||
case ItemType.InputBool:
|
||||
jObject["value"] = ((BoolItem)item).Value;
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jackett.Services
|
||||
{
|
||||
public interface IProtectionService
|
||||
{
|
||||
string Protect(string plainText);
|
||||
string UnProtect(string plainText);
|
||||
}
|
||||
|
||||
public class ProtectionService : IProtectionService
|
||||
{
|
||||
DataProtectionScope PROTECTION_SCOPE = DataProtectionScope.LocalMachine;
|
||||
const string APPLICATION_KEY = "Dvz66r3n8vhTGip2/quiw5ISyM37f7L2iOdupzdKmzkvXGhAgQiWK+6F+4qpxjPVNks1qO7LdWuVqRlzgLzeW8mChC6JnBMUS1Fin4N2nS9lh4XPuCZ1che75xO92Nk2vyXUo9KSFG1hvEszAuLfG2Mcg1r0sVyVXd2gQDU/TbY=";
|
||||
|
||||
IServerService serverService;
|
||||
|
||||
public ProtectionService(IServerService s)
|
||||
{
|
||||
serverService = s;
|
||||
|
||||
if (System.Environment.OSVersion.Platform == PlatformID.Unix)
|
||||
{
|
||||
// We should not be running as root and will only have access to the local store.
|
||||
PROTECTION_SCOPE = DataProtectionScope.CurrentUser;
|
||||
}
|
||||
}
|
||||
|
||||
public string Protect(string plainText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(plainText))
|
||||
return string.Empty;
|
||||
|
||||
var plainBytes = Encoding.UTF8.GetBytes(plainText);
|
||||
var appKey = Convert.FromBase64String(APPLICATION_KEY);
|
||||
var instanceKey = Encoding.UTF8.GetBytes(serverService.Config.InstanceId);
|
||||
var entropy = new byte[appKey.Length + instanceKey.Length];
|
||||
Buffer.BlockCopy(instanceKey, 0, entropy, 0, instanceKey.Length);
|
||||
Buffer.BlockCopy(appKey, 0, entropy, instanceKey.Length, appKey.Length);
|
||||
|
||||
var protectedBytes = ProtectedData.Protect(plainBytes, entropy, PROTECTION_SCOPE);
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (RijndaelManaged AES = new RijndaelManaged())
|
||||
{
|
||||
AES.KeySize = 256;
|
||||
AES.BlockSize = 128;
|
||||
|
||||
var key = new Rfc2898DeriveBytes(instanceKey, instanceKey.Reverse().ToArray(), 64);
|
||||
AES.Key = key.GetBytes(AES.KeySize / 8);
|
||||
AES.IV = key.GetBytes(AES.BlockSize / 8);
|
||||
|
||||
AES.Mode = CipherMode.CBC;
|
||||
|
||||
using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(protectedBytes, 0, protectedBytes.Length);
|
||||
cs.Close();
|
||||
}
|
||||
protectedBytes = ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(protectedBytes);
|
||||
}
|
||||
|
||||
public string UnProtect(string plainText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(plainText))
|
||||
return string.Empty;
|
||||
|
||||
var protectedBytes = Convert.FromBase64String(plainText);
|
||||
var instanceKey = Encoding.UTF8.GetBytes(serverService.Config.InstanceId);
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (RijndaelManaged AES = new RijndaelManaged())
|
||||
{
|
||||
AES.KeySize = 256;
|
||||
AES.BlockSize = 128;
|
||||
|
||||
var key = new Rfc2898DeriveBytes(instanceKey, instanceKey.Reverse().ToArray(), 64);
|
||||
AES.Key = key.GetBytes(AES.KeySize / 8);
|
||||
AES.IV = key.GetBytes(AES.BlockSize / 8);
|
||||
|
||||
AES.Mode = CipherMode.CBC;
|
||||
|
||||
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
|
||||
{
|
||||
cs.Write(protectedBytes, 0, protectedBytes.Length);
|
||||
cs.Close();
|
||||
}
|
||||
protectedBytes = ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
var appKey = Convert.FromBase64String(APPLICATION_KEY);
|
||||
var entropy = new byte[appKey.Length + instanceKey.Length];
|
||||
Buffer.BlockCopy(instanceKey, 0, entropy, 0, instanceKey.Length);
|
||||
Buffer.BlockCopy(appKey, 0, entropy, instanceKey.Length, appKey.Length);
|
||||
|
||||
var unprotectedBytes = ProtectedData.Unprotect(protectedBytes, entropy, PROTECTION_SCOPE);
|
||||
return Encoding.UTF8.GetString(unprotectedBytes);
|
||||
}
|
||||
|
||||
public void Protect<T>(T obj)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
|
||||
foreach(var property in type.GetProperties(BindingFlags.SetProperty |BindingFlags.GetProperty | BindingFlags.Public))
|
||||
{
|
||||
if(property.GetCustomAttributes(typeof(JackettProtectedAttribute), false).Count() > 0)
|
||||
{
|
||||
var value = property.GetValue(obj);
|
||||
if(value is string)
|
||||
{
|
||||
var protectedString = Protect(value as string);
|
||||
property.SetValue(obj, protectedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UnProtect<T>(T obj)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
|
||||
foreach (var property in type.GetProperties(BindingFlags.SetProperty | BindingFlags.GetProperty | BindingFlags.Public))
|
||||
{
|
||||
if (property.GetCustomAttributes(typeof(JackettProtectedAttribute), false).Count() > 0)
|
||||
{
|
||||
var value = property.GetValue(obj);
|
||||
if (value is string)
|
||||
{
|
||||
var unprotectedString = UnProtect(value as string);
|
||||
property.SetValue(obj, unprotectedString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using Autofac;
|
||||
using Jackett.Models.Config;
|
||||
using Jackett.Services;
|
||||
using Jackett.Utils;
|
||||
using Jackett.Utils.Clients;
|
||||
using Microsoft.Owin.Hosting;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
@ -16,6 +17,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -91,12 +93,16 @@ namespace Jackett.Services
|
|||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.APIKey))
|
||||
{
|
||||
config.APIKey = config.GenerateApi();
|
||||
}
|
||||
config.APIKey = StringUtil.GenerateRandom(32);
|
||||
|
||||
configService.SaveConfig<ServerConfig>(config);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.InstanceId))
|
||||
{
|
||||
config.InstanceId = StringUtil.GenerateRandom(64);
|
||||
configService.SaveConfig<ServerConfig>(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveConfig()
|
||||
|
|
|
@ -51,7 +51,6 @@ namespace Jackett.Utils
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
public static string GetExceptionDetails(this Exception exception)
|
||||
{
|
||||
var properties = exception.GetType()
|
||||
|
@ -74,5 +73,21 @@ namespace Jackett.Utils
|
|||
{
|
||||
return string.Join("&", collection.AllKeys.Select(a => a + "=" + HttpUtility.UrlEncode(collection[a])));
|
||||
}
|
||||
|
||||
public static string GenerateRandom(int length)
|
||||
{
|
||||
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var randBytes = new byte[length];
|
||||
using (var rngCsp = new RNGCryptoServiceProvider())
|
||||
{
|
||||
rngCsp.GetBytes(randBytes);
|
||||
var key = "";
|
||||
foreach (var b in randBytes)
|
||||
{
|
||||
key += chars[b % chars.Length];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue