diff --git a/src/Standard.Licensing.Tests/JsonSerializeTests.cs b/src/Standard.Licensing.Tests/JsonSerializeTests.cs new file mode 100644 index 0000000..fcd1a90 --- /dev/null +++ b/src/Standard.Licensing.Tests/JsonSerializeTests.cs @@ -0,0 +1,146 @@ +// +// Copyright © 2012 - 2013 Nauck IT KG http://www.nauck-it.de +// +// Author: +// Daniel Nauck +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Xml.Linq; +using NUnit.Framework; + +namespace Standard.Licensing.Tests +{ + [TestFixture] + public class JsonSerializeTests + { + private string passPhrase; + private string privateKey; + private string publicKey; + + [SetUp] + public void Init() + { + passPhrase = Guid.NewGuid().ToString(); + var keyGenerator = Security.Cryptography.KeyGenerator.Create(); + var keyPair = keyGenerator.GenerateKeyPair(); + privateKey = keyPair.ToEncryptedPrivateKeyString(passPhrase); + publicKey = keyPair.ToPublicKeyString(); + } + + private static DateTime ConvertToRfc1123(DateTime dateTime) + { + return DateTime.ParseExact( + dateTime.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture) + , "r", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + } + + + [Test] + public void Can_Generate_And_Validate_Signature_With_Empty_License() + { + var license = License.New() + .CreateAndSignWithPrivateKey(privateKey, passPhrase); + + Assert.That(license, Is.Not.Null); + Assert.That(license.Signature, Is.Not.Null); + + // validate json + var jsonText = license.ToJson(); + Assert.That(jsonText, Is.Not.Empty); + + + + // validate default values when not set + Assert.That(license.Id, Is.EqualTo(Guid.Empty)); + Assert.That(license.Type, Is.EqualTo(LicenseType.Trial)); + Assert.That(license.Quantity, Is.EqualTo(0)); + Assert.That(license.ProductFeatures, Is.Null); + Assert.That(license.Customer, Is.Null); + Assert.That(license.Expiration, Is.EqualTo(ConvertToRfc1123(DateTime.MaxValue))); + + // verify signature + Assert.That(license.VerifySignature(publicKey), Is.True); + } + + [Test] + public void Can_Generate_And_Validate_Signature_With_Standard_License() + { + var licenseId = Guid.NewGuid(); + var customerName = "Max Mustermann"; + var customerEmail = "max@mustermann.tld"; + var expirationDate = DateTime.Now.AddYears(1); + var productFeatures = new Dictionary + { + {"Sales Module", "yes"}, + {"Purchase Module", "yes"}, + {"Maximum Transactions", "10000"} + }; + + var additionalAttributes = new Dictionary + { + {"Domain", "test.com"}, + {"Host", "172.17.0.1"} + }; + + var license = License.New() + .WithUniqueIdentifier(licenseId) + .As(LicenseType.Standard) + .WithMaximumUtilization(10) + .WithProductFeatures(productFeatures) + .WithAdditionalAttributes(additionalAttributes) + .LicensedTo(customerName, customerEmail) + .ExpiresAt(expirationDate) + .CreateAndSignWithPrivateKey(privateKey, passPhrase); + + Assert.That(license, Is.Not.Null); + Assert.That(license.Signature, Is.Not.Null); + + + // validate json + var jsonText = license.ToJson(); + Assert.That(jsonText, Is.Not.Empty); + + var parsedLicense = jsonText.ToLicense(); + Assert.That(parsedLicense, Is.Not.Null); + + + // validate default values when not set + Assert.That(parsedLicense.Id, Is.EqualTo(licenseId)); + Assert.That(parsedLicense.Type, Is.EqualTo(LicenseType.Standard)); + Assert.That(parsedLicense.Quantity, Is.EqualTo(10)); + Assert.That(parsedLicense.ProductFeatures, Is.Not.Null); + Assert.That(parsedLicense.ProductFeatures.GetAll(), Is.EquivalentTo(productFeatures)); + Assert.That(parsedLicense.Customer, Is.Not.Null); + Assert.That(parsedLicense.Customer.Name, Is.EqualTo(customerName)); + Assert.That(parsedLicense.Customer.Email, Is.EqualTo(customerEmail)); + Assert.That(parsedLicense.Expiration, Is.EqualTo(ConvertToRfc1123(expirationDate))); + + // verify signature + Assert.That(parsedLicense.VerifySignature(publicKey), Is.True); + } + + + + } +} \ No newline at end of file diff --git a/src/Standard.Licensing/JsonSerializeExtension.cs b/src/Standard.Licensing/JsonSerializeExtension.cs new file mode 100644 index 0000000..43e7911 --- /dev/null +++ b/src/Standard.Licensing/JsonSerializeExtension.cs @@ -0,0 +1,205 @@ +// +// Copyright © 2012 - 2013 Nauck IT KG http://www.nauck-it.de +// +// Author: +// Daniel Nauck +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.Json; +using System.Xml.Linq; + +namespace Standard.Licensing +{ + + /// + /// serialize as JSON / deserialize from JSON + /// + public static class JsonSerializeExtension + { + /// + /// serialize as JSON + /// + /// License instance + /// JSON string + public static string ToJson(this License license) + { + try + { + var jsonObj = new LicenseModel + { + Id = license.Id, + Type = license.Type, + Quantity = license.Quantity, + Signature = license.Signature, + Expiration = license.Expiration + }; + + if (license.AdditionalAttributes != null) + { + jsonObj.AdditionalAttributes = license.AdditionalAttributes.GetAll(); + } + if (license.Customer != null) + { + jsonObj.Customer = new Dictionary { + { "Name", license.Customer.Name }, + { "Company", license.Customer.Company }, + { "Email", license.Customer.Email } + }; + } + if (license.ProductFeatures != null) + { + jsonObj.ProductFeatures = license.ProductFeatures.GetAll(); + } + + return JsonSerializer.Serialize(jsonObj); + } + catch (Exception) + { + return string.Empty; + } + } + + /// + /// deserialize from JSON + /// + /// JSON string + /// License instance + public static License ToLicense(this string sourceString) + { + try + { + var jsonObj = JsonSerializer.Deserialize(sourceString); + var xml = new XElement("License"); + xml.SetTag("Id", jsonObj.Id.ToString()); + xml.SetTag("Type", jsonObj.Type.ToString()); + xml.SetTag("Quantity", jsonObj.Quantity.ToString()); + + if (jsonObj.ProductFeatures != null) + { + var xmlFeatures = xml.Element("ProductFeatures"); + if (xmlFeatures == null) + { + xml.Add(new XElement("ProductFeatures")); + } + xmlFeatures = xml.Element("ProductFeatures"); + foreach (var item in jsonObj.ProductFeatures) + { + if (!string.IsNullOrEmpty(item.Value)) + { + xmlFeatures.SetChildTag("Feature", item.Key, item.Value); + } + } + } + + + + + + if (jsonObj.AdditionalAttributes != null) + { + var xmlAdditional = xml.Element("LicenseAttributes"); + if (xmlAdditional == null) + { + xml.Add(new XElement("LicenseAttributes")); + } + xmlAdditional = xml.Element("LicenseAttributes"); + foreach (var item in jsonObj.AdditionalAttributes) + { + if (!string.IsNullOrEmpty(item.Value)) + { + xmlAdditional.SetChildTag("Attribute", item.Key, item.Value); + } + } + } + + if (jsonObj.Customer != null) + { + var xmlCustomer = xml.Element("Customer"); + if (xmlCustomer == null) + { + xml.Add(new XElement("Customer")); + } + xmlCustomer = xml.Element("Customer"); + foreach (var item in jsonObj.Customer) + { + if (!string.IsNullOrEmpty(item.Value)) + { + var node = new XElement(item.Key); + node.SetValue(item.Value); + xmlCustomer.Add(node); + } + } + } + xml.SetTag("Signature", jsonObj.Signature); + xml.SetTag("Expiration", jsonObj.Expiration.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture)); + + + return new License(xml); + } + catch (Exception ex) + { + return null; + } + } + + + public static void SetTag(this XElement xmlData, string name, string value) + { + var element = xmlData.Element(name); + + if (element == null) + { + element = new XElement(name); + xmlData.Add(element); + } + + if (value != null) + element.Value = value; + } + + public static string GetTag(this XElement xmlData, string name) + { + var element = xmlData.Element(name); + return element != null ? element.Value : null; + } + + public static void SetChildTag(this XElement xmlData, string childName, string name, string value) + { + var element = + xmlData.Elements(childName) + .FirstOrDefault(e => e.Attribute("name") != null && e.Attribute("name").Value == name); + + if (element == null) + { + element = new XElement(childName); + element.Add(new XAttribute("name", name)); + xmlData.Add(element); + } + + if (value != null) + element.Value = value; + } + } +} diff --git a/src/Standard.Licensing/LicenseModel.cs b/src/Standard.Licensing/LicenseModel.cs new file mode 100644 index 0000000..66b9001 --- /dev/null +++ b/src/Standard.Licensing/LicenseModel.cs @@ -0,0 +1,44 @@ +// +// Copyright © 2012 - 2013 Nauck IT KG http://www.nauck-it.de +// +// Author: +// Daniel Nauck +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace Standard.Licensing +{ + public class LicenseModel + { + public IDictionary AdditionalAttributes { get; set; } + public IDictionary Customer { get; set; } + public DateTime Expiration { get; set; } + public Guid Id { get; set; } + public IDictionary ProductFeatures { get; set; } + public int Quantity { get; set; } + public string Signature { get; set; } + public LicenseType Type { get; set; } + + + } +} \ No newline at end of file diff --git a/src/Standard.Licensing/Standard.Licensing.csproj b/src/Standard.Licensing/Standard.Licensing.csproj index d5f2477..a358f49 100644 --- a/src/Standard.Licensing/Standard.Licensing.csproj +++ b/src/Standard.Licensing/Standard.Licensing.csproj @@ -51,6 +51,7 @@ v1.1.9 +