Compare commits

..

4 Commits
1.0.1 ... 1.0.2

23 changed files with 279 additions and 64 deletions

View File

@@ -27,8 +27,8 @@ namespace IDeliverable.Licensing.Orchard
{
var productManifest = mProducts.Single(x => x.ProductId == mProductId);
var licenseSettingsUrl = mUrlHelper.Action("Index", "Admin", new { area = "Settings", groupInfoId = "Licenses" });
var refreshResultUrl = mUrlHelper.Action("VerifyLicenseKey", "LicenseValidation", new { area = productManifest.ProductName });
var refreshResultLink = T("<a href=\"{0}\">Refresh</a>", refreshResultUrl);
var retryResultUrl = mUrlHelper.Action("VerifyLicenseKey", "LicenseValidation", new { area = productManifest.ProductName });
var retryResultLink = T("<a href=\"{0}\">Try again</a>", retryResultUrl);
LocalizedString message = null;
try
@@ -40,13 +40,16 @@ namespace IDeliverable.Licensing.Orchard
switch (ex.Error)
{
case LicenseValidationError.UnknownLicenseKey:
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module is invalid. {2}", licenseSettingsUrl, productManifest.ProductName, refreshResultLink);
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module is invalid. {2}", licenseSettingsUrl, productManifest.ProductName, retryResultLink);
break;
case LicenseValidationError.HostnameMismatch:
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module is invalid for the current host name. {2}", licenseSettingsUrl, productManifest.ProductName, refreshResultLink);
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module is invalid for the current host name. {2}", licenseSettingsUrl, productManifest.ProductName, retryResultLink);
break;
case LicenseValidationError.NoActiveSubscription:
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module has no active subscription. {2}", licenseSettingsUrl, productManifest.ProductName, refreshResultLink);
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module has no active subscription. {2}", licenseSettingsUrl, productManifest.ProductName, retryResultLink);
break;
case LicenseValidationError.LicenseRevoked:
message = T("The <a href=\"{0}\">configured license key</a> for the {1} module has been revoked. {2}", licenseSettingsUrl, productManifest.ProductName, retryResultLink);
break;
case LicenseValidationError.TokenAgeValidationFailed:
case LicenseValidationError.TokenSignatureValidationFailed:
@@ -54,11 +57,11 @@ namespace IDeliverable.Licensing.Orchard
case LicenseValidationError.LicensingServiceUnreachable:
case LicenseValidationError.UnexpectedError:
default:
message = T("There was an error validating the <a href=\"{0}\">configured license key</a> for the {1} module. {2}", licenseSettingsUrl, productManifest.ProductName, refreshResultLink);
message = T("There was an error validating the <a href=\"{0}\">configured license key</a> for the {1} module. {2}", licenseSettingsUrl, productManifest.ProductName, retryResultLink);
break;
}
Logger.Warning(ex, "An error occurred while validating the configured license key for the {0} module. {1}", productManifest.ProductName, refreshResultLink);
Logger.Warning(ex, "An error occurred while validating the configured license key for the {0} module. {1}", productManifest.ProductName, retryResultLink);
}
if (message != null)

View File

@@ -3,8 +3,6 @@
<!--
Learn more about Application Insights configuration with ApplicationInsights.config here:
http://go.microsoft.com/fwlink/?LinkID=513840
Note: If not present, please add <InstrumentationKey>Your Key</InstrumentationKey> to the top of this file.
-->
<TelemetryModules>
<Add Type="Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.DiagnosticsTelemetryModule, Microsoft.ApplicationInsights" />
@@ -52,6 +50,4 @@
<Add Type="Microsoft.ApplicationInsights.Extensibility.Web.TelemetryInitializers.WebUserTelemetryInitializer, Microsoft.ApplicationInsights.Extensibility.Web" />
<Add Type="Microsoft.ApplicationInsights.Extensibility.Web.TelemetryInitializers.WebSessionTelemetryInitializer, Microsoft.ApplicationInsights.Extensibility.Web" />
</TelemetryInitializers>
<!-- This key is for Application Insights resource 'ideliverable-licensing' in resource group 'Internal' -->
<InstrumentationKey>4af2a280-2e97-489d-be03-52ed20a87ae4</InstrumentationKey>
</ApplicationInsights>

View File

@@ -3,7 +3,7 @@ using System.Configuration;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using IDeliverable.Licensing.Service.Exceptions;
using IDeliverable.Licensing.Exceptions;
using IDeliverable.Licensing.Service.Services;
using IDeliverable.Licensing.VerificationTokens;
@@ -15,36 +15,36 @@ namespace IDeliverable.Licensing.Service.Controllers
[HttpGet]
[Route("license/{licenseKey}/verify")]
[ApiKeyAuthorization]
public LicenseVerificationToken Verify(string licenseKey, int productId, string hostname)
public LicenseVerificationToken Verify(string licenseKey, string productId, string hostname)
{
var sendOwlApiEndpoint = ConfigurationManager.AppSettings["SendOwlApiEndpoint"];
var sendOwlApiKey = ConfigurationManager.AppSettings["SendOwlApiKey"];
var sendOwlApiSecret = ConfigurationManager.AppSettings["SendOwlApiSecret"];
var tokenSigningCertificateThumbprint = ConfigurationManager.AppSettings["TokenSigningCertificateThumbprint"];
var service = new LicenseService(sendOwlApiEndpoint, sendOwlApiKey, sendOwlApiSecret, tokenSigningCertificateThumbprint);
var service = GetLicenseService();
try
{
return service.VerifyLicense(licenseKey, productId, hostname);
return service.VerifyLicense(licenseKey, productId, hostname, throwOnError: false);
}
catch (LicenseVerificationException ex)
{
switch (ex.Error)
{
case LicenseVerificationError.UnknownLicenseKey:
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound) { ReasonPhrase = ex.Message });
ThrowHttpResponseException(ex, HttpStatusCode.NotFound);
break;
case LicenseVerificationError.HostnameMismatch:
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Forbidden) { ReasonPhrase = ex.Message });
ThrowHttpResponseException(ex, HttpStatusCode.Forbidden);
break;
case LicenseVerificationError.NoActiveSubscription:
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Gone) { ReasonPhrase = ex.Message });
case LicenseVerificationError.LicenseRevoked:
ThrowHttpResponseException(ex, HttpStatusCode.Gone);
break;
default:
throw;
}
}
return null;
}
[HttpGet]
@@ -52,13 +52,14 @@ namespace IDeliverable.Licensing.Service.Controllers
[ApiKeyAuthorization]
public HttpResponseMessage Test()
{
var testKey = ConfigurationManager.AppSettings["TestKey"];
var testProductId = ConfigurationManager.AppSettings["TestProductId"];
var testHostname = ConfigurationManager.AppSettings["TestHostname"];
var testKey = ConfigurationManager.AppSettings["TestKey"];
var service = GetLicenseService();
try
{
var token = Verify(testKey, Int32.Parse(testProductId), testHostname);
service.VerifyLicense(testKey, testProductId, testHostname, throwOnError: true);
}
catch (Exception ex)
{
@@ -67,5 +68,22 @@ namespace IDeliverable.Licensing.Service.Controllers
return new HttpResponseMessage(HttpStatusCode.OK);
}
private static LicenseService GetLicenseService()
{
var sendOwlApiEndpoint = ConfigurationManager.AppSettings["SendOwlApiEndpoint"];
var sendOwlApiKey = ConfigurationManager.AppSettings["SendOwlApiKey"];
var sendOwlApiSecret = ConfigurationManager.AppSettings["SendOwlApiSecret"];
var tokenSigningCertificateThumbprint = ConfigurationManager.AppSettings["TokenSigningCertificateThumbprint"];
return new LicenseService(sendOwlApiEndpoint, sendOwlApiKey, sendOwlApiSecret, tokenSigningCertificateThumbprint);
}
private static void ThrowHttpResponseException(LicenseVerificationException ex, HttpStatusCode httpStatusCode)
{
var response = new HttpResponseMessage(httpStatusCode) { ReasonPhrase = ex.Message };
response.Headers.Add("LicensingErrorCode", ex.Error.ToString());
throw new HttpResponseException(response);
}
}
}

View File

@@ -1,6 +1,7 @@
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System;
using System.Configuration;
using System.Web.Http;
using Microsoft.ApplicationInsights.Extensibility;
namespace IDeliverable.Licensing.Service
{
@@ -9,6 +10,13 @@ namespace IDeliverable.Licensing.Service
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
// Configure ApplicationInsights instrumentation key.
var instrumentationKey = ConfigurationManager.AppSettings["ApplicationInsights.InstrumentationKey"];
if (!String.IsNullOrWhiteSpace(instrumentationKey))
TelemetryConfiguration.Active.InstrumentationKey = instrumentationKey;
else
TelemetryConfiguration.Active.DisableTelemetry = true;
}
}
}

View File

@@ -43,6 +43,18 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="LogentriesCore, Version=2.6.7.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\logentries.core.2.7.0\lib\net40\LogentriesCore.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="LogentriesLog4net, Version=2.6.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\logentries.log4net.2.7.0\lib\net40\LogentriesLog4net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.ApplicationInsights, Version=0.14.3.177, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.ApplicationInsights.0.14.3-build00177\lib\net40\Microsoft.ApplicationInsights.dll</HintPath>
<Private>True</Private>
@@ -87,6 +99,10 @@
<Reference Include="Microsoft.Web.Infrastructure">
<HintPath>..\..\..\lib\aspnetmvc\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.WindowsAzure.Configuration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.WindowsAzure.ConfigurationManager.3.1.0\lib\net40\Microsoft.WindowsAzure.Configuration.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll</HintPath>
</Reference>
@@ -147,14 +163,13 @@
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="ApiKeyAuthorizationAttribute.cs" />
<Compile Include="Controllers\LicenseController.cs" />
<Compile Include="Exceptions\LicenseVerificationError.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\LicenseInfo.cs" />
<Compile Include="Services\LicenseService.cs" />
<Compile Include="Exceptions\LicenseVerificationException.cs" />
<Compile Include="Services\Logger.cs" />
<Compile Include="Services\OrderInfo.cs" />
<Compile Include="Services\OrderStatus.cs" />
</ItemGroup>
@@ -162,6 +177,7 @@
<Content Include="Global.asax" />
<Content Include="packages.config" />
<Content Include="ApplicationInsights.config" />
<Content Include="Log4net.config" />
<None Include="Properties\PublishProfiles\ideliverable-licensing.pubxml" />
<Content Include="Web.config">
<SubType>Designer</SubType>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<root>
<!-- Value of level may be ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF -->
<level value="DEBUG" />
<appender-ref ref="DebuggerAppender" />
<appender-ref ref="LeAppender" />
</root>
<appender name="LeAppender" type="log4net.Appender.LogentriesAppender, LogentriesLog4net">
<!-- Credentials are read from appSettings. -->
<immediateFlush value="true" />
<debug value="true" />
<useHttpPut value="false" />
<useSsl value="false" />
<layout type="log4net.Layout.PatternLayout">
<!-- The below pattern has been carefully formatted and optimized to work well with the Logentries.com entry parser. For reference see https://logentries.com/doc/search/. -->
<param name="ConversionPattern" value="%d %logger %level% %m%n%exceptionSessionId='%aspnet-request{ASP.NET_SessionId}'; Username='%aspnet-request{AUTH_USER}'; ClientIpAddress='%aspnet-request{REMOTE_ADDR}'; ClientUserAgent='%aspnet-request{HTTP_USER_AGENT}'; ServerName='%aspnet-request{SERVER_NAME}'; RequestMethod='%aspnet-request{REQUEST_METHOD}'; RequestUrl='%aspnet-request{URL}'; RequestQueryString='%aspnet-request{QUERY_STRING}'; RequestCookies='%aspnet-request{HTTP_COOKIE}'; MachineName='%property{log4net:HostName}';%n" />
</layout>
</appender>
<appender name="DebuggerAppender" type="log4net.Appender.DebugAppender">
<immediateFlush value="true" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
</log4net>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>MSDeploy</WebPublishMethod>
<ADUsesOwinOrOpenIdConnect>False</ADUsesOwinOrOpenIdConnect>
<LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish>http://ideliverable-licensing.azurewebsites.net</SiteUrlToLaunchAfterPublish>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<MSDeployServiceURL>ideliverable-licensing.scm.azurewebsites.net:443</MSDeployServiceURL>
<DeployIisAppPath>ideliverable-licensing</DeployIisAppPath>
<RemoteSitePhysicalPath />
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
<EnableMSDeployBackup>True</EnableMSDeployBackup>
<UserName>$ideliverable-licensing</UserName>
<_SavePWD>True</_SavePWD>
<_DestinationType>AzureWebSite</_DestinationType>
</PropertyGroup>
</Project>

View File

@@ -5,7 +5,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using IDeliverable.Licensing.Service.Exceptions;
using IDeliverable.Licensing.Exceptions;
using IDeliverable.Licensing.VerificationTokens;
using Newtonsoft.Json.Linq;
@@ -19,15 +19,19 @@ namespace IDeliverable.Licensing.Service.Services
mSendOwlApiKey = sendOwlApiKey;
mSendOwlApiSecret = sendOwlApiSecret;
mTokenSigningCertificateThumbprint = tokenSigningCertificateThumbprint;
mLogger = new Logger(nameof(LicenseService));
}
private readonly string mSendOwlApiEndpoint;
private readonly string mSendOwlApiKey;
private readonly string mSendOwlApiSecret;
private readonly string mTokenSigningCertificateThumbprint;
private readonly Logger mLogger;
public LicenseVerificationToken VerifyLicense(string licenseKey, int productId, string hostname)
public LicenseVerificationToken VerifyLicense(string licenseKey, string productId, string hostname, bool throwOnError)
{
mLogger.Info($"Starting license verification... LicenseKey={licenseKey}; ProductId={productId}; Hostname={hostname};");
try
{
using (var client = new HttpClient())
@@ -45,23 +49,33 @@ namespace IDeliverable.Licensing.Service.Services
var order = ParseOrderInfo(client.GetAsync($"orders/{orderId}").Result.Content.ReadAsStringAsync().Result);
if (!order.IsProductAccessAllowed)
throw new LicenseVerificationException($"The license with key '{licenseKey}' is not associated with an active subscription.", LicenseVerificationError.NoActiveSubscription);
{
if(order.SubscriptionId != null)
throw new LicenseVerificationException($"The license with key '{licenseKey}' is not associated with an active subscription.", LicenseVerificationError.NoActiveSubscription);
throw new LicenseVerificationException($"The license with key '{licenseKey}' has been revoked.", LicenseVerificationError.LicenseRevoked);
}
if (!order.Hostnames.Contains(hostname, StringComparer.OrdinalIgnoreCase))
throw new LicenseVerificationException($"The license with key '{licenseKey}' is not valid for the provided '{hostname}'. Valid hostnames are '{String.Join(",", order.Hostnames)}", LicenseVerificationError.HostnameMismatch);
var token = CreateVerificationToken(productId, hostname, licenseKey);
return token;
}
}
catch (Exception ex) when (!(ex is LicenseVerificationException))
{
throw new LicenseVerificationException(LicenseVerificationError.UnhandledException, ex);
mLogger.Error($"An error occurred while verifying license with SendOwl. LicenseKey={licenseKey}; ProductId={productId}; Hostname={hostname};", ex);
if (throwOnError)
throw;
mLogger.Info("Ignoring error and issuing verification token anyway. LicenseKey={licenseKey}; ProductId={productId}; Hostname={hostname};");
}
mLogger.Info($"License verification finished. LicenseKey={licenseKey}; ProductId={productId}; Hostname={hostname};");
return CreateVerificationToken(productId, hostname, licenseKey);
}
private LicenseVerificationToken CreateVerificationToken(int productId, string hostname, string key)
private LicenseVerificationToken CreateVerificationToken(string productId, string hostname, string key)
{
X509Certificate2 signingCert = null;
@@ -107,11 +121,12 @@ namespace IDeliverable.Licensing.Service.Services
return licensesQuery.ToArray();
}
private OrderInfo ParseOrderInfo(string json)
private static OrderInfo ParseOrderInfo(string json)
{
var order = JObject.Parse(json)["order"];
var status = ReadOrderStatus(order);
var isAccessAllowed = (bool) order["access_allowed"];
var subscriptionId = (int?) order["subscription_id"];
var customFields = (JArray)order["order_custom_checkout_fields"];
var hostnames = new List<string>();
@@ -121,7 +136,7 @@ namespace IDeliverable.Licensing.Service.Services
if (customFields.Count > 1)
hostnames.Add((string)customFields[1]["order_custom_checkout_field"]["value"]);
return new OrderInfo((int)order["id"], status, isAccessAllowed, hostnames);
return new OrderInfo((int)order["id"], status, isAccessAllowed, subscriptionId, hostnames);
}
private static OrderStatus ReadOrderStatus(JToken order)

View File

@@ -0,0 +1,64 @@
using System;
using System.IO;
using log4net;
using log4net.Config;
namespace IDeliverable.Licensing.Service.Services
{
public class Logger
{
private const string ConfigFileName = "Log4net.config";
static Logger()
{
var basePath = AppDomain.CurrentDomain.BaseDirectory;
var configFilePath = Path.Combine(basePath, ConfigFileName);
if (File.Exists(configFilePath))
XmlConfigurator.Configure(new FileInfo(configFilePath));
else
throw new Exception($"The log4net configuration file could not be found at path '{configFilePath}'.");
}
public Logger(Type loggerType)
:this(loggerType.FullName)
{
}
public Logger(string loggerName)
{
mLog = LogManager.GetLogger(loggerName);
}
private ILog mLog;
public void Fatal(string message, Exception ex = null)
{
if (mLog.IsFatalEnabled)
mLog.Fatal(message, ex);
}
public void Error(string message, Exception ex = null)
{
if (mLog.IsErrorEnabled)
mLog.Error(message, ex);
}
public void Warn(string message, Exception ex = null)
{
if (mLog.IsWarnEnabled)
mLog.Warn(message, ex);
}
public void Info(string message, Exception ex = null)
{
if (mLog.IsInfoEnabled)
mLog.Info(message, ex);
}
public void Debug(string message, Exception ex = null)
{
if (mLog.IsDebugEnabled)
mLog.Debug(message, ex);
}
}
}

View File

@@ -4,17 +4,19 @@ namespace IDeliverable.Licensing.Service.Services
{
internal class OrderInfo
{
public OrderInfo(int orderId, OrderStatus status, bool isProductAccessAllowed, IEnumerable<string> hostnames)
public OrderInfo(int orderId, OrderStatus status, bool isProductAccessAllowed, int? subscriptionId, IEnumerable<string> hostnames)
{
OrderId = orderId;
Hostnames = hostnames;
Status = status;
IsProductAccessAllowed = isProductAccessAllowed;
SubscriptionId = subscriptionId;
}
public int OrderId { get; }
public IEnumerable<string> Hostnames { get; }
public OrderStatus Status { get; }
public bool IsProductAccessAllowed { get; }
public int? SubscriptionId { get; }
}
}

View File

@@ -2,7 +2,6 @@
<configuration>
<appSettings>
<!-- Below three settings can be overridden through the Azure portal. -->
<add key="ApiKey" value="MJb17j7YAzSjyEYkhsoI" />
<add key="TestProductId" value="233554" />
<add key="TestHostname" value="ideliverable.com" />
@@ -11,6 +10,8 @@
<add key="SendOwlApiKey" value="14be9ee5e05222e" />
<add key="SendOwlApiSecret" value="1a10fe7987724e0a56a1" />
<add key="TokenSigningCertificateThumbprint" value="fa4746a778716109e7e80e1b8dc2ed2a2ba3b852" />
<add key="Logentries.Token" value="5c73ab7f-39a3-4171-b730-03da5c3d4b24"/> <!-- Local -->
<add key="ApplicationInsights.InstrumentationKey" value="" /> <!-- Disabled -->
</appSettings>
<system.web>

View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.3" targetFramework="net452" userInstalled="true" />
<package id="logentries.core" version="2.7.0" targetFramework="net452" userInstalled="true" />
<package id="logentries.log4net" version="2.7.0" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.ApplicationInsights" version="0.14.3-build00177" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="0.14.0-build08008" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.ApplicationInsights.JavaScript" version="0.11.0-build09387" targetFramework="net452" userInstalled="true" />
@@ -20,5 +23,6 @@
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Diagnostics.Tracing.EventSource.Redist" version="1.1.16-beta" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.WindowsAzure.ConfigurationManager" version="3.1.0" targetFramework="net452" userInstalled="true" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net452" userInstalled="true" />
</packages>

View File

@@ -1,9 +1,10 @@
namespace IDeliverable.Licensing.Service.Exceptions
namespace IDeliverable.Licensing.Exceptions
{
internal enum LicenseVerificationError
public enum LicenseVerificationError
{
UnknownLicenseKey,
NoActiveSubscription,
LicenseRevoked,
HostnameMismatch,
UnhandledException
}

View File

@@ -1,8 +1,8 @@
using System;
namespace IDeliverable.Licensing.Service.Exceptions
namespace IDeliverable.Licensing.Exceptions
{
internal class LicenseVerificationException : Exception
public class LicenseVerificationException : Exception
{
public LicenseVerificationException(LicenseVerificationError error)
:this("An error occurred while verifying the license.", error)

View File

@@ -46,6 +46,8 @@
<ItemGroup>
<Compile Include="CacheInvalidationContext.cs" />
<Compile Include="CacheService.cs" />
<Compile Include="Exceptions\LicenseVerificationError.cs" />
<Compile Include="Exceptions\LicenseVerificationException.cs" />
<Compile Include="HttpContextAccessor.cs" />
<Compile Include="HttpRequestExtensions.cs" />
<Compile Include="Validation\LicenseValidationError.cs" />

View File

@@ -5,6 +5,7 @@ namespace IDeliverable.Licensing.Validation
UnknownLicenseKey,
HostnameMismatch,
NoActiveSubscription,
LicenseRevoked,
LicensingServiceError,
LicensingServiceUnreachable,
TokenAgeValidationFailed,

View File

@@ -56,6 +56,10 @@ namespace IDeliverable.Licensing.Validation
error = LicenseValidationError.NoActiveSubscription;
break;
case LicenseVerificationTokenError.LicenseRevoked:
error = LicenseValidationError.LicenseRevoked;
break;
case LicenseVerificationTokenError.LicenseServiceError:
error = LicenseValidationError.LicensingServiceError;
break;

View File

@@ -5,7 +5,7 @@ namespace IDeliverable.Licensing.VerificationTokens
{
public class LicenseVerificationInfo
{
public LicenseVerificationInfo(int productId, string hostname, string licenseKey, long verifiedUtcTicks)
public LicenseVerificationInfo(string productId, string hostname, string licenseKey, long verifiedUtcTicks)
{
ProductId = productId;
Hostname = hostname;
@@ -13,7 +13,7 @@ namespace IDeliverable.Licensing.VerificationTokens
VerifiedUtcTicks = verifiedUtcTicks;
}
public int ProductId { get; }
public string ProductId { get; }
public string Hostname { get; }
public string LicenseKey { get; }
public long VerifiedUtcTicks { get; }

View File

@@ -46,7 +46,8 @@ namespace IDeliverable.Licensing.VerificationTokens
var lvtex = (LicenseVerificationTokenException)ex;
if (lvtex.Error == LicenseVerificationTokenError.UnknownLicenseKey ||
lvtex.Error == LicenseVerificationTokenError.HostnameMismatch ||
lvtex.Error == LicenseVerificationTokenError.NoActiveSubscription)
lvtex.Error == LicenseVerificationTokenError.NoActiveSubscription ||
lvtex.Error == LicenseVerificationTokenError.LicenseRevoked)
{
mStore.Clear(productId);
throw;

View File

@@ -5,6 +5,7 @@ namespace IDeliverable.Licensing.VerificationTokens
UnknownLicenseKey,
HostnameMismatch,
NoActiveSubscription,
LicenseRevoked,
LicenseServiceError,
LicenseServiceUnreachable,
UnexpectedError

View File

@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using IDeliverable.Licensing.Exceptions;
namespace IDeliverable.Licensing.VerificationTokens
{
@@ -35,7 +38,7 @@ namespace IDeliverable.Licensing.VerificationTokens
try
{
HttpResponseMessage response = null;
HttpResponseMessage response;
try
{
@@ -46,17 +49,28 @@ namespace IDeliverable.Licensing.VerificationTokens
throw new LicenseVerificationTokenException("An error occurred while calling the licensing service.", LicenseVerificationTokenError.LicenseServiceUnreachable, ex);
}
if (response.StatusCode == HttpStatusCode.NotFound)
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.UnknownLicenseKey);
if (response.StatusCode == HttpStatusCode.Forbidden)
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.HostnameMismatch);
if (response.StatusCode == HttpStatusCode.Gone)
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.NoActiveSubscription);
if (!response.IsSuccessStatusCode)
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.LicenseServiceError);
{
var licenseVerificationError = ReadLicenseVerificationError(response);
switch (licenseVerificationError)
{
case LicenseVerificationError.UnknownLicenseKey:
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.UnknownLicenseKey);
case LicenseVerificationError.HostnameMismatch:
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.HostnameMismatch);
case LicenseVerificationError.NoActiveSubscription:
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.NoActiveSubscription);
case LicenseVerificationError.LicenseRevoked:
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.LicenseRevoked);
default:
throw new LicenseVerificationTokenException(response.ReasonPhrase, LicenseVerificationTokenError.LicenseServiceError);
}
}
var responseText = response.Content.ReadAsStringAsync().Result;
var token = LicenseVerificationToken.Parse(responseText);
@@ -65,9 +79,19 @@ namespace IDeliverable.Licensing.VerificationTokens
}
finally
{
ServicePointManager.ServerCertificateValidationCallback -= certValidationHandler;
ServicePointManager.ServerCertificateValidationCallback -= certValidationHandler;
}
}
}
private static LicenseVerificationError? ReadLicenseVerificationError(HttpResponseMessage response)
{
IEnumerable<string> values;
if (!response.Headers.TryGetValues("LicensingErrorCode", out values))
return null;
var value = values.First();
return (LicenseVerificationError)Enum.Parse(typeof(LicenseVerificationError), value);
}
}
}

View File

@@ -5,7 +5,8 @@ namespace IDeliverable.Slides.Licensing
{
public class LicenseValidationController : LicenseValidationControllerBase
{
public LicenseValidationController(INotifier notifier) : base(notifier)
public LicenseValidationController(INotifier notifier)
: base(notifier)
{
}

View File

@@ -18,7 +18,7 @@ namespace IDeliverable.Slides.Licensing
private readonly IOrchardServices _orchardServices;
string ILicensedProductManifest.ProductId => ProductId;
string ILicensedProductManifest.ProductName => ProductName;
bool ILicensedProductManifest.SkipValidationForLocalRequests => true;
bool ILicensedProductManifest.SkipValidationForLocalRequests => false;
public string LicenseKey => _orchardServices.WorkContext.CurrentSite.As<SlidesLicenseSettingsPart>().LicenseKey;
}