-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added command line argument to configure registration contact
added command line argument to configure agreement uri removed hard coded contact email improved unit tests
- Loading branch information
Showing
12 changed files
with
309 additions
and
356 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,9 +14,12 @@ public class Options | |
[Value(0, HelpText = "The names of the domains for which you want to request a certificate.", Required = true, MetaName = "domains")] | ||
public IEnumerable<string> Domains { get; set; } | ||
|
||
[Option('a', "acceptTOS", HelpText = "Accept the terms of service of the ACME server")] | ||
[Option('a', "acceptTermsOfService", HelpText = "Accept the terms of service of the ACME server")] | ||
public bool AcceptTermsOfService { get; set; } | ||
|
||
[Option('t', "termsOfServiceUri", HelpText = "The uri of the terms of service that you accept.", Default = "https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf")] | ||
public string TermsOfServiceUri { get; set; } | ||
|
||
[Option('i', "ignoreSSLErrors", HelpText = "Ignore SSL certificate errors for the HTTPS connection to the ACME server (useful for debugging messages with fiddler)")] | ||
public bool IgnoreSSLCertificateErrors { get; set; } | ||
|
||
|
@@ -43,5 +46,8 @@ public class Options | |
|
||
[Option('b', "iisBinding", HelpText = "The IIS binding that should be configured to use the new certificate. Syntax: ip:port:hostname, for example '*:443:www.example.com' (used with --serverConfigurationProvider iis). If you do not specifiy a binding, the provider will try to find a matching binding. It will create a binding if no matching binding exists.")] | ||
public string IISBinidng { get; set; } | ||
|
||
[Option('m', "contact", HelpText = "The contact information for the registration request. Example: mailto:[email protected]")] | ||
public string[] Contact { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,32 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Formatting; | ||
using System.Security.Cryptography; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using Oocx.ACME.Client; | ||
using Oocx.ACME.Protocol; | ||
using WorldDomination.Net.Http; | ||
using Oocx.ACME.Tests.FakeHttp; | ||
using Xunit; | ||
|
||
namespace Oocx.ACME.Tests | ||
{ | ||
public class ClientTests | ||
{ | ||
|
||
|
||
[Fact] | ||
public async Task Should_get_a_Directory_object_from_the_default_endpoint() | ||
{ | ||
// Arrange | ||
var http = new FakeHttpMessageHandler("http://baseaddress/"); | ||
var directory = new Directory(); | ||
var response = new HttpResponseMessage(HttpStatusCode.OK) {Content = new ObjectContent<Directory>(directory, new JsonMediaTypeFormatter()) }; | ||
response.Headers.Add("Replay-Nonce", "nonce"); | ||
var handler = new FakeHttpMessageHandler("http://baseaddress/directory", response); | ||
var client = new HttpClient(handler) { BaseAddress = new Uri("http://baseAddress")}; | ||
|
||
var client = | ||
http.RequestTo("directory").Returns(directory).WithNonce("nonce") | ||
.GetHttpClient(); | ||
|
||
var sut = new AcmeClient(client, new RSACryptoServiceProvider()); | ||
|
||
// Act | ||
|
@@ -37,28 +40,62 @@ public async Task Should_get_a_Directory_object_from_the_default_endpoint() | |
public async Task Should_post_a_valid_Registration_message() | ||
{ | ||
// Arrange | ||
var http = new FakeHttpMessageHandler("http://baseaddress/"); | ||
var directory = new Directory() { NewRegistration = new Uri("http://baseaddress/registration")}; | ||
var response1 = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ObjectContent<Directory>(directory, new JsonMediaTypeFormatter()) }; | ||
response1.Headers.Add("Replay-Nonce", "nonce"); | ||
|
||
var registration = new RegistrationResponse(); | ||
var response2 = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ObjectContent<RegistrationResponse>(registration, new JsonMediaTypeFormatter()) }; | ||
response2.Headers.Add("Replay-Nonce", "nonce"); | ||
|
||
var handler = new FakeHttpMessageHandler(new Dictionary<string, HttpResponseMessage>() | ||
var client = | ||
http.RequestTo("directory").Returns(directory).WithNonce("nonce"). | ||
RequestTo("registration").Returns(registration).WithNonce("nonce"). | ||
GetHttpClient(); | ||
|
||
var sut = new AcmeClient(client, new RSACryptoServiceProvider()); | ||
|
||
// Act | ||
var registrationResponse = await sut.RegisterAsync("agreementUri", new []{ "mailto:[email protected]"}); | ||
|
||
// Assert | ||
registrationResponse.Should().NotBeNull(); | ||
http.ReceivedRequestsTo("directory").Single().HasMethod(HttpMethod.Get); | ||
http.ReceivedRequestsTo("registration").Single().HasMethod(HttpMethod.Post).HasJwsPayload<NewRegistrationRequest>(r => | ||
{ | ||
{"http://baseaddress/directory", response1 }, | ||
{"http://baseaddress/registration", response2 } | ||
r.Agreement.Should().Be("agreementUri"); | ||
r.Contact.Should().Contain("mailto:[email protected]"); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public async Task Should_POST_to_get_registration_details_if_the_registration_already_exists() | ||
{ | ||
// Arrange | ||
var http = new FakeHttpMessageHandler("http://baseaddress/"); | ||
var directory = new Directory() { NewRegistration = new Uri("http://baseaddress/registration") }; | ||
var registration = new RegistrationResponse(); | ||
|
||
var client = | ||
http.RequestTo("directory").Returns(directory).WithNonce("nonce"). | ||
RequestTo("registration").Returns(new Problem(), "application/problem+json").HasStatusCode(HttpStatusCode.Conflict).WithHeader("Location", "http://baseaddress/existingreguri").WithNonce("nonce"). | ||
RequestTo("existingreguri").Returns(registration).WithNonce("nonce"). | ||
GetHttpClient(); | ||
|
||
var client = new HttpClient(handler) { BaseAddress = new Uri("http://baseAddress") }; | ||
var sut = new AcmeClient(client, new RSACryptoServiceProvider()); | ||
|
||
// Act | ||
var registrationResponse = await sut.RegisterAsync(false); | ||
var registrationResponse = await sut.RegisterAsync("agreementUri", new[] { "mailto:[email protected]" }); | ||
|
||
// Assert | ||
registrationResponse.Should().NotBeNull(); | ||
http.ReceivedRequestsTo("directory").Single().HasMethod(HttpMethod.Get); | ||
http.ReceivedRequestsTo("registration").Single().HasMethod(HttpMethod.Post).HasJwsPayload<NewRegistrationRequest>(r => | ||
{ | ||
r.Agreement.Should().Be("agreementUri"); | ||
r.Contact.Should().Contain("mailto:[email protected]"); | ||
}); | ||
http.ReceivedRequestsTo("existingreguri").Single().HasMethod(HttpMethod.Post).HasJwsPayload<UpdateRegistrationRequest>(r => | ||
{ | ||
r.Agreement.Should().BeNull(); | ||
r.Contact.Should().BeNull(); | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using FluentAssertions.Execution; | ||
|
||
namespace Oocx.ACME.Tests.FakeHttp | ||
{ | ||
public class FakeHttpMessageHandler : HttpMessageHandler | ||
{ | ||
private readonly Queue<FakeRequestConfiguration> requestConfigurations = new Queue<FakeRequestConfiguration>(); | ||
|
||
private readonly List<HttpRequestMessage> actualRequests = new List<HttpRequestMessage>(); | ||
|
||
public string BaseAddress { get; } | ||
|
||
public FakeHttpMessageHandler(string baseAddress) | ||
{ | ||
this.BaseAddress = baseAddress; | ||
} | ||
|
||
public FakeRequestConfiguration RequestTo(string uri) | ||
{ | ||
return new FakeRequestConfiguration(this) { Uri = BaseAddress + uri }; | ||
} | ||
|
||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | ||
{ | ||
request.Content = await PreventDisposeContentWrapper.CreateWrapperAsync(request.Content); | ||
|
||
actualRequests.Add(request); | ||
|
||
var expected = requestConfigurations.Dequeue(); | ||
if (!request.RequestUri.Equals(new Uri(expected.Uri, UriKind.RelativeOrAbsolute))) | ||
{ | ||
throw new AssertionFailedException($"expected request to '{expected.Uri}', but got request to '{request.RequestUri}'"); | ||
} | ||
|
||
return expected.GetResponseMessage(); | ||
} | ||
|
||
public HttpRequestMessage[] ReceivedRequestsTo(string uri) | ||
{ | ||
var requests = actualRequests.Where(r => r.RequestUri.Equals(new Uri(BaseAddress + uri))).ToArray(); | ||
if (!requests.Any()) | ||
{ | ||
throw new AssertionFailedException($"expected a request to {uri}, but only got the following requests: {string.Join(", ", actualRequests.Select(r => r.RequestUri.ToString()))}"); | ||
} | ||
return requests; | ||
} | ||
|
||
public void Enqueue(FakeRequestConfiguration fakeRequestConfiguration) | ||
{ | ||
requestConfigurations.Enqueue(fakeRequestConfiguration); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Formatting; | ||
|
||
namespace Oocx.ACME.Tests.FakeHttp | ||
{ | ||
public class FakeRequestConfiguration | ||
{ | ||
private readonly FakeHttpMessageHandler fakeHttpMessageHandler; | ||
private static readonly JsonMediaTypeFormatter Formatter = new JsonMediaTypeFormatter(); | ||
|
||
public FakeRequestConfiguration(FakeHttpMessageHandler fakeHttpMessageHandler) | ||
{ | ||
this.fakeHttpMessageHandler = fakeHttpMessageHandler; | ||
fakeHttpMessageHandler.Enqueue(this); | ||
} | ||
|
||
public string Uri { get; set; } | ||
|
||
public ObjectContent Content { get; set; } | ||
|
||
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>(); | ||
|
||
public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK; | ||
|
||
public FakeRequestConfiguration Returns<T>(T content, string contentType = "application/json") | ||
{ | ||
Content = new ObjectContent<T>(content, Formatter); | ||
Content.Headers.ContentType.MediaType = contentType; | ||
return this; | ||
} | ||
|
||
public FakeRequestConfiguration HasStatusCode(HttpStatusCode statusCode) | ||
{ | ||
StatusCode = statusCode; | ||
return this; | ||
} | ||
|
||
public FakeRequestConfiguration WithHeader(string key, string value) | ||
{ | ||
Headers[key] = value; | ||
return this; | ||
} | ||
|
||
public FakeRequestConfiguration WithNonce(string value) | ||
{ | ||
Headers["Replay-Nonce"] = value; | ||
return this; | ||
} | ||
|
||
public FakeRequestConfiguration RequestTo(string uri) | ||
{ | ||
return fakeHttpMessageHandler.RequestTo(uri); | ||
} | ||
|
||
public HttpResponseMessage GetResponseMessage() | ||
{ | ||
var response = new HttpResponseMessage(StatusCode) { Content = Content}; | ||
foreach (var header in Headers) | ||
{ | ||
response.Headers.Add(header.Key, header.Value); | ||
} | ||
return response; | ||
} | ||
|
||
public HttpClient GetHttpClient() | ||
{ | ||
var client = new HttpClient(fakeHttpMessageHandler) { BaseAddress = new Uri(fakeHttpMessageHandler.BaseAddress, UriKind.Absolute) }; | ||
return client; | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/Oocx.ACME.Tests/FakeHttp/HttpRequestMessageAssertions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using System; | ||
using System.Net.Http; | ||
using System.Text; | ||
using FluentAssertions; | ||
using Newtonsoft.Json; | ||
using Oocx.ACME.Jose; | ||
using Oocx.Asn1PKCS.Asn1BaseTypes; | ||
|
||
namespace Oocx.ACME.Tests.FakeHttp | ||
{ | ||
public static class HttpRequestMessageAssertions | ||
{ | ||
public static HttpRequestMessage HasContent(this HttpRequestMessage request, Action<HttpContent> contentAssertion) | ||
{ | ||
contentAssertion(request.Content); | ||
return request; | ||
} | ||
|
||
public static HttpRequestMessage HasContent<T>(this HttpRequestMessage request, Action<T> contentAssertion) | ||
{ | ||
contentAssertion(request.Content.ReadAsAsync<T>().Result); | ||
return request; | ||
} | ||
|
||
public static HttpRequestMessage HasJwsPayload<T>(this HttpRequestMessage request, Action<T> contentAssertion) | ||
{ | ||
var message = request.Content.ReadAsAsync<JWSMessage>().Result; | ||
var contentJson = Encoding.UTF8.GetString(message.Payload.Base64UrlDecode()); | ||
var content = JsonConvert.DeserializeObject<T>(contentJson); | ||
|
||
contentAssertion(content); | ||
return request; | ||
} | ||
|
||
public static HttpRequestMessage HasHeader(this HttpRequestMessage request, string headerName, string headerValue) | ||
{ | ||
request.Headers.Should().Contain(headerName); | ||
request.Headers.GetValues(headerName).Should().Contain(headerValue); | ||
return request; | ||
} | ||
|
||
public static HttpRequestMessage HasMethod(this HttpRequestMessage request, HttpMethod method) | ||
{ | ||
request.Method.Should().Be(method); | ||
return request; | ||
} | ||
} | ||
} |
Oops, something went wrong.