Skip to content

Commit

Permalink
Adding support for HTTPS Protocol, Certificates and SNI.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeme authored and davidebbo committed Mar 31, 2015
1 parent d6d0d4a commit d42e4fb
Show file tree
Hide file tree
Showing 67 changed files with 2,376 additions and 408 deletions.
162 changes: 162 additions & 0 deletions Kudu.SiteManagement.Test/Certificates/CertificateLookupFacts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using Kudu.SiteManagement.Certificates;
using Kudu.SiteManagement.Certificates.Wrappers;
using Kudu.SiteManagement.Test.Certificates.Fakes;
using Moq;
using Xunit;

namespace Kudu.SiteManagement.Test.Certificates
{
public class CertificateLookupFacts
{
[Fact]
public void ByThumbprint_OneMatching_ReturnsCertificate()
{
Mock<IX509Store> storeMock = new Mock<IX509Store>();
IX509Certificate2Collection collectionFake = new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake(),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "FindMe")
};
storeMock.Setup(mock => mock.Certificates).Returns(collectionFake);

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My }, name => storeMock.Object)
.ByThumbprint();

Assert.Equal(result.FriendlyName, "FindMe");
Assert.Equal(result.Thumbprint, "FindMe");
}

[Fact]
public void ByFriendlyName_OneMatching_ReturnsCertificate()
{
Mock<IX509Store> storeMock = new Mock<IX509Store>();
storeMock.Setup(mock => mock.Certificates).Returns(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake(),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "FindMe")
});

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My }, name => storeMock.Object)
.ByFriendlyName();

Assert.Equal(result.FriendlyName, "FindMe");
Assert.Equal(result.Thumbprint, "FindMe");
}

[Fact]
public void ByThumbprint_NoneMatching_ReturnsNull()
{
Mock<IX509Store> storeMock = new Mock<IX509Store>();
IX509Certificate2Collection collectionFake = new X509Certificate2CollectionFake { new X509Certificate2Fake() };
storeMock.Setup(mock => mock.Certificates).Returns(collectionFake);

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My }, name => storeMock.Object)
.ByThumbprint();

Assert.Null(result);
}

[Fact]
public void ByFriendlyName_NoneMatching_ReturnsNull()
{
Mock<IX509Store> storeMock = new Mock<IX509Store>();
storeMock.Setup(mock => mock.Certificates).Returns(new X509Certificate2CollectionFake { new X509Certificate2Fake() });

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My }, name => storeMock.Object)
.ByFriendlyName();

Assert.Null(result);
}

[Fact]
public void ByThumbprint_MultipleMatching_ReturnsFirstMatchingCertificate()
{
Mock<IX509Store> storeMock = new Mock<IX509Store>();
storeMock.Setup(mock => mock.Certificates).Returns(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "FindMe"),
new X509Certificate2Fake(friendlyName: "NotMe", thumbprint: "FindMe")
});

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My }, name => storeMock.Object)
.ByThumbprint();

Assert.Equal(result.FriendlyName, "FindMe");
Assert.Equal(result.Thumbprint, "FindMe");
}

[Fact]
public void ByFriendlyName_MultipleMatching_ReturnsFirstMatchingCertificate()
{
Mock<IX509Store> storeMock = new Mock<IX509Store>();
storeMock.Setup(mock => mock.Certificates).Returns(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "FindMe"),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "NotMe")
});

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My }, name => storeMock.Object)
.ByFriendlyName();

Assert.Equal(result.FriendlyName, "FindMe");
Assert.Equal(result.Thumbprint, "FindMe");
}

[Fact]
public void ByThumbprint_OneMatchingInSecondaryStore_ReturnsCertificate()
{
Dictionary<StoreName, Mock<IX509Store>> storeMocks = new Dictionary<StoreName, Mock<IX509Store>>();
storeMocks[StoreName.My] = CreateX509StoreMock(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake()
});
storeMocks[StoreName.Root] = CreateX509StoreMock(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "FindMe")
});

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My, StoreName.Root }, name => storeMocks[name].Object)
.ByThumbprint();

Assert.Equal(result.FriendlyName, "FindMe");
Assert.Equal(result.Thumbprint, "FindMe");
}

[Fact]
public void ByFriendlyName_OneMatchingInSecondaryStore_ReturnsCertificate()
{
Dictionary<StoreName, Mock<IX509Store>> storeMocks = new Dictionary<StoreName, Mock<IX509Store>>();
storeMocks[StoreName.My] = CreateX509StoreMock(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake()
});
storeMocks[StoreName.Root] = CreateX509StoreMock(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(),
new X509Certificate2Fake(friendlyName: "FindMe", thumbprint: "FindMe")
});

Certificate result = new CertificateLookup("FindMe", new[] { StoreName.My, StoreName.Root }, name => storeMocks[name].Object)
.ByFriendlyName();

Assert.Equal(result.FriendlyName, "FindMe");
Assert.Equal(result.Thumbprint, "FindMe");
}

private static Mock<IX509Store> CreateX509StoreMock(IX509Certificate2Collection fake)
{
Mock<IX509Store> mock = new Mock<IX509Store>();
mock.Setup(m => m.Certificates).Returns(fake);
return mock;
}
}
}
63 changes: 63 additions & 0 deletions Kudu.SiteManagement.Test/Certificates/CertificateSearcherFacts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Kudu.SiteManagement.Certificates;
using Kudu.SiteManagement.Certificates.Wrappers;
using Kudu.SiteManagement.Configuration;
using Kudu.SiteManagement.Test.Certificates.Fakes;
using Moq;
using Xunit;

namespace Kudu.SiteManagement.Test.Certificates
{
public class CertificateSearcherFacts
{
[Fact]
public void FindAll_MultipleStores_ReturnsFromAllStores()
{
Mock<IKuduConfiguration> configMock = new Mock<IKuduConfiguration>();
configMock.Setup(mock => mock.CertificateStores)
.Returns(new[] { new CertificateStoreConfiguration(StoreName.My), new CertificateStoreConfiguration(StoreName.Root) });

Dictionary<StoreName, Mock<IX509Store>> storeMocks = new Dictionary<StoreName, Mock<IX509Store>>();
storeMocks[StoreName.My] = CreateX509StoreMock(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(friendlyName: "My_CertA"),
new X509Certificate2Fake(friendlyName: "My_CertB")
});
storeMocks[StoreName.Root] = CreateX509StoreMock(new X509Certificate2CollectionFake
{
new X509Certificate2Fake(friendlyName: "Root_CertA"),
new X509Certificate2Fake(friendlyName: "Root_CertB")
});

ICertificateSearcher searcher = new CertificateSearcher(configMock.Object, name => storeMocks[name].Object);
Dictionary<string, Certificate> all = searcher.FindAll().ToDictionary(c => c.FriendlyName);

Assert.Equal(all.Count, 4);
Assert.NotNull(all["My_CertA"]);
Assert.NotNull(all["My_CertB"]);
Assert.NotNull(all["Root_CertA"]);
Assert.NotNull(all["Root_CertB"]);
}

[Fact]
public void Lookup_ReturnsCertificateLookupObject()
{
Mock<IKuduConfiguration> configMock = new Mock<IKuduConfiguration>();
configMock.Setup(mock => mock.CertificateStores).Returns(new[] { new CertificateStoreConfiguration(StoreName.My) });

ICertificateSearcher searcher = new CertificateSearcher(configMock.Object, null);
ICertificateLookup result = searcher.Lookup("FindMe");

Assert.IsType<CertificateLookup>(result);
}

private static Mock<IX509Store> CreateX509StoreMock(IX509Certificate2Collection fake)
{
Mock<IX509Store> mock = new Mock<IX509Store>();
mock.Setup(m => m.Certificates).Returns(fake);
return mock;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Kudu.SiteManagement.Certificates.Wrappers;

namespace Kudu.SiteManagement.Test.Certificates.Fakes
{
//Note: Simple Mock class instead of having to mock the interface, since IX509Certificate2 essentially is
// a "data collection" and not a Service, this can sometimes be easier this way.
public class X509Certificate2CollectionFake : List<IX509Certificate2>, IX509Certificate2Collection
{
public X509Certificate2CollectionFake()
{
}

public X509Certificate2CollectionFake(IEnumerable<IX509Certificate2> collection)
: base(collection)
{
}

public IX509Certificate2Collection Find(X509FindType findType, object findValue, bool validOnly)
{
switch (findType)
{
case X509FindType.FindByThumbprint:
return new X509Certificate2CollectionFake(this.Where(c => c.Thumbprint == (string)findValue));
case X509FindType.FindBySubjectName:
case X509FindType.FindBySubjectDistinguishedName:
case X509FindType.FindByIssuerName:
case X509FindType.FindByIssuerDistinguishedName:
case X509FindType.FindBySerialNumber:
case X509FindType.FindByTimeValid:
case X509FindType.FindByTimeNotYetValid:
case X509FindType.FindByTimeExpired:
case X509FindType.FindByTemplateName:
case X509FindType.FindByApplicationPolicy:
case X509FindType.FindByCertificatePolicy:
case X509FindType.FindByExtension:
case X509FindType.FindByKeyUsage:
case X509FindType.FindBySubjectKeyIdentifier:
throw new NotImplementedException();
default:
throw new ArgumentOutOfRangeException("findType");
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Kudu.SiteManagement.Certificates.Wrappers;

namespace Kudu.SiteManagement.Test.Certificates.Fakes
{
//Note: Simple Mock class instead of having to mock the interface, since IX509Certificate2 essentially is
// a "data object" and not a Service, this can sometimes be easier this way.
public class X509Certificate2Fake : IX509Certificate2
{
private byte[] hash;

public string Thumbprint { get; private set; }
public string FriendlyName { get; private set; }

public X509Certificate2Fake(string friendlyName = "DummyFriendlyName", string thumbprint = "DummyThumbprint", byte[] hash = null)
{
Thumbprint = thumbprint;
FriendlyName = friendlyName;
this.hash = hash ?? new byte[0];
}

public byte[] GetCertHash()
{
return hash;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Kudu.SiteManagement.Configuration.Section.Bindings;

namespace Kudu.SiteManagement.Test.Configuration.Fakes
{
public class ApplicationBindingConfigurationElementFake : ApplicationBindingConfigurationElement
{
public ApplicationBindingConfigurationElementFake SetFake(string key, object value)
{
this[key] = value;
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Kudu.SiteManagement.Configuration.Section.Bindings;

namespace Kudu.SiteManagement.Test.Configuration.Fakes
{
public class BindingsConfigurationElementCollectionFake : BindingsConfigurationElementCollection
{
public BindingsConfigurationElementCollectionFake AddFake(BindingConfigurationElement element)
{
base.Add(element);
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Kudu.SiteManagement.Configuration.Section.Cert;

namespace Kudu.SiteManagement.Test.Configuration.Fakes
{
public class CertificateStoreConfigurationElementFake : CertificateStoreConfigurationElement
{
public CertificateStoreConfigurationElementFake SetFake(string key, object value)
{
this[key] = value;
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Kudu.SiteManagement.Configuration.Section.Cert;

namespace Kudu.SiteManagement.Test.Configuration.Fakes
{
public class CertificateStoresConfigurationElementCollectionFake : CertificateStoresConfigurationElementCollection
{
public CertificateStoresConfigurationElementCollectionFake AddFake(CertificateStoreConfigurationElement element)
{
base.Add(element);
return this;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Kudu.SiteManagement.Configuration.Section;

namespace Kudu.SiteManagement.Test.Configuration.Fakes
{
public class KuduConfigurationSectionFake : KuduConfigurationSection
{
public KuduConfigurationSectionFake SetFake(string key, object value)
{
this[key] = value;
return this;
}
}
}
Loading

0 comments on commit d42e4fb

Please sign in to comment.