NUC-5: TelemetryPublisherFirestore now always checks for auth.

This commit is contained in:
Denis-Cosmin Nutiu 2020-10-31 16:55:16 +02:00
parent 44ee9c2071
commit 0f373783df
2 changed files with 42 additions and 30 deletions

View file

@ -32,6 +32,7 @@ namespace NucuCar.Telemetry.Publishers
protected HttpClient HttpClient; protected HttpClient HttpClient;
private string _idToken; private string _idToken;
private DateTime _nextExpiresTime;
// Variables used for authentication // Variables used for authentication
private readonly string _webEmail; private readonly string _webEmail;
@ -73,7 +74,13 @@ namespace NucuCar.Telemetry.Publishers
private async Task SetupAuthorization() private async Task SetupAuthorization()
{ {
// Make request // Check if the token is about to expire in the next 5 minutes.
if (DateTime.UtcNow.AddMinutes(5) < _nextExpiresTime)
{
return;
}
// https://cloud.google.com/identity-platform/docs/use-rest-api#section-sign-in-email-password
var requestUrl = $"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={_webApiKey}"; var requestUrl = $"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={_webApiKey}";
var data = new Dictionary<string, object>() var data = new Dictionary<string, object>()
{ {
@ -88,6 +95,9 @@ namespace NucuCar.Telemetry.Publishers
{ {
var jsonContent = await response.GetJson(); var jsonContent = await response.GetJson();
_idToken = jsonContent.GetProperty("idToken").ToString(); _idToken = jsonContent.GetProperty("idToken").ToString();
// Setup next expire.
var expiresIn = double.Parse(jsonContent.GetProperty("expiresIn").ToString());
_nextExpiresTime = DateTime.UtcNow.AddSeconds(expiresIn);
HttpClient.Authorization(_idToken); HttpClient.Authorization(_idToken);
} }
else else
@ -109,6 +119,7 @@ namespace NucuCar.Telemetry.Publishers
HttpResponseMessage responseMessage = null; HttpResponseMessage responseMessage = null;
try try
{ {
await SetupAuthorization();
responseMessage = await HttpClient.PostAsync("", data); responseMessage = await HttpClient.PostAsync("", data);
} }
// ArgumentException occurs during json serialization errors. // ArgumentException occurs during json serialization errors.

View file

@ -79,6 +79,9 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Telemetry.Tests
}; };
var publisher = new MockTelemetryPublisherFirestore(opts); var publisher = new MockTelemetryPublisherFirestore(opts);
var mockHttpClient = new MockHttpClient("http://testing.com"); var mockHttpClient = new MockHttpClient("http://testing.com");
var authResponse = new HttpResponseMessage(HttpStatusCode.OK)
{Content = new StringContent("{\"idToken\": \"1\",\"expiresIn\": \"3600\"}")};
mockHttpClient.SendAsyncResponses.Add(authResponse);
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK)); mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK));
publisher.SetHttpClient(mockHttpClient); publisher.SetHttpClient(mockHttpClient);
publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1}); publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1});
@ -87,7 +90,7 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Telemetry.Tests
await publisher.PublishAsync(CancellationToken.None); await publisher.PublishAsync(CancellationToken.None);
// Assert // Assert
var request = mockHttpClient.SendAsyncArgCalls[0]; var request = mockHttpClient.SendAsyncArgCalls[1];
Assert.Equal(HttpMethod.Post, request.Method); Assert.Equal(HttpMethod.Post, request.Method);
Assert.Equal(new Uri("http://testing.com"), request.RequestUri); Assert.Equal(new Uri("http://testing.com"), request.RequestUri);
Assert.Equal("{\"fields\":{\"testData\":{\"integerValue\":1}}}", Assert.Equal("{\"fields\":{\"testData\":{\"integerValue\":1}}}",
@ -104,6 +107,9 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Telemetry.Tests
}; };
var publisher = new MockTelemetryPublisherFirestore(opts); var publisher = new MockTelemetryPublisherFirestore(opts);
var mockHttpClient = new MockHttpClient("http://testing.com"); var mockHttpClient = new MockHttpClient("http://testing.com");
var authResponse = new HttpResponseMessage(HttpStatusCode.OK)
{Content = new StringContent("{\"idToken\": \"1\",\"expiresIn\": \"3600\"}")};
mockHttpClient.SendAsyncResponses.Add(authResponse);
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK)); mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK));
publisher.SetHttpClient(mockHttpClient); publisher.SetHttpClient(mockHttpClient);
publisher.SetMockData(new Dictionary<string, object> {["testData"] = double.PositiveInfinity}); publisher.SetMockData(new Dictionary<string, object> {["testData"] = double.PositiveInfinity});
@ -111,8 +117,8 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Telemetry.Tests
// Run // Run
await publisher.PublishAsync(CancellationToken.None); await publisher.PublishAsync(CancellationToken.None);
// Assert no request made. // Assert only auth request made.
Assert.Empty(mockHttpClient.SendAsyncArgCalls); Assert.Single(mockHttpClient.SendAsyncArgCalls);
} }
[Fact] [Fact]
@ -140,7 +146,7 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Telemetry.Tests
} }
[Fact] [Fact]
private async Task Test_PublishAsync_Authorization_OK() private async Task Test_PublishAsync_Authorization_Refresh()
{ {
// Setup // Setup
var opts = new TelemetryPublisherOptions() var opts = new TelemetryPublisherOptions()
@ -150,44 +156,39 @@ namespace NucuCar.UnitTests.NucuCar.Domain.Telemetry.Tests
}; };
var publisher = new MockTelemetryPublisherFirestore(opts); var publisher = new MockTelemetryPublisherFirestore(opts);
var mockHttpClient = new MockHttpClient("http://testing.com"); var mockHttpClient = new MockHttpClient("http://testing.com");
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.Forbidden));
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("{\"idToken\":\"testauthtoken\"}")
});
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK));
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK)
{Content = new StringContent("{\"idToken\": \"1\",\"expiresIn\": \"0\"}")});
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK));
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK)
{Content = new StringContent("{\"idToken\": \"1\",\"expiresIn\": \"3600\"}")});
mockHttpClient.SendAsyncResponses.Add(new HttpResponseMessage(HttpStatusCode.OK));
publisher.SetHttpClient(mockHttpClient); publisher.SetHttpClient(mockHttpClient);
publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1}); publisher.SetMockData(new Dictionary<string, object> {["testData"] = 1});
// Run // Run
await publisher.PublishAsync(CancellationToken.None); await publisher.PublishAsync(CancellationToken.None);
await publisher.PublishAsync(CancellationToken.None);
// Assert // Assert
Assert.Equal(3, mockHttpClient.SendAsyncArgCalls.Count); Assert.Equal(4, mockHttpClient.SendAsyncArgCalls.Count);
// 1st request - auth denied // 1st request auth
Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[0].Method); Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[0].Method);
Assert.Equal(new Uri("http://testing.com"), mockHttpClient.SendAsyncArgCalls[0].RequestUri); Assert.Equal("https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=TAPIKEY",
Assert.Equal("{\"fields\":{\"testData\":{\"integerValue\":1}}}", mockHttpClient.SendAsyncArgCalls[0].RequestUri.ToString());
mockHttpClient.SendAsyncArgCalls[0].Content.ReadAsStringAsync().GetAwaiter().GetResult());
// 2st request - authorizing // 2st request payload
Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[1].Method); Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[1].Method);
Assert.Equal(new Uri("https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=TAPIKEY"),
mockHttpClient.SendAsyncArgCalls[1].RequestUri);
Assert.Equal("{\"email\":\"t@emai.com\",\"password\":\"tpass\",\"returnSecureToken\":true}",
mockHttpClient.SendAsyncArgCalls[1].Content.ReadAsStringAsync().GetAwaiter().GetResult());
// 3rd request auth
// 3st request with authorization
Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[2].Method); Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[2].Method);
Assert.Equal(new Uri("http://testing.com"), mockHttpClient.SendAsyncArgCalls[2].RequestUri); Assert.Equal("https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=TAPIKEY",
Assert.Equal("{\"fields\":{\"testData\":{\"integerValue\":1}}}", mockHttpClient.SendAsyncArgCalls[2].RequestUri.ToString());
mockHttpClient.SendAsyncArgCalls[2].Content.ReadAsStringAsync().GetAwaiter().GetResult());
Assert.Equal(new AuthenticationHeaderValue("Bearer", "testauthtoken"), // 4th request payload
mockHttpClient.SendAsyncArgCalls[2].Headers.Authorization); Assert.Equal(HttpMethod.Post, mockHttpClient.SendAsyncArgCalls[1].Method);
} }
} }
} }