Skip to content

Commit

Permalink
Merge branch 'release-0.10.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
dnl-blkv committed Aug 23, 2017
2 parents deb6cbe + 8516528 commit 325af4c
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 32 deletions.
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions BunqSdk.Tests/Context/ApiContextTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Bunq.Sdk.Context;
using Xunit;

namespace Bunq.Sdk.Tests.Context
{
/// <summary>
/// Tests:
/// ApiContext
/// </summary>
public class ApiContextTest : BunqSdkTestBase, IClassFixture<ApiContextTest>
{
/// <summary>
/// Path to a temporary context file.
/// </summary>
private const string CONTEXT_FILENAME_TEST = "context-save-restore-test.conf";

private static ApiContext apiContext;

public ApiContextTest()
{
if (apiContext == null) apiContext = GetApiContext();
}

/// <summary>
/// Tests serialization and de-serialization of the API context.
/// </summary>
[Fact]
public void TestApiContextSerializeDeserialize()
{
var apiContextJson = apiContext.ToJson();
var apiContextDeSerialised = ApiContext.FromJson(apiContextJson);

Assert.Equal(apiContextJson, apiContextDeSerialised.ToJson());
}

/// <summary>
/// Tests saving and restoring of the API context.
/// </summary>
[Fact]
public void TestApiContextSaveRestore()
{
var apiContextJson = apiContext.ToJson();
apiContext.Save(CONTEXT_FILENAME_TEST);
var apiContextRestored = ApiContext.Restore(CONTEXT_FILENAME_TEST);

Assert.Equal(apiContextJson, apiContextRestored.ToJson());
}
}
}
2 changes: 1 addition & 1 deletion BunqSdk/BunqSdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageId>Bunq.Sdk</PackageId>
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>0.9.2.0</VersionPrefix>
<VersionPrefix>0.10.0.0</VersionPrefix>
<VersionSuffix>beta</VersionSuffix>
</PropertyGroup>
<PropertyGroup>
Expand Down
33 changes: 26 additions & 7 deletions BunqSdk/Context/ApiContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public class ApiContext
[JsonProperty(PropertyName = "session_context")]
public SessionContext SessionContext { get; private set; }

[JsonProperty(PropertyName = "proxy")]
public string Proxy { get; private set; }

[JsonConstructor]
private ApiContext()
{
Expand All @@ -71,21 +74,23 @@ private ApiContext()
/// <summary>
/// Create and initialize an API Context with current IP as permitted.
/// </summary>
public static ApiContext Create(ApiEnvironmentType environmentType, string apiKey, string deviceDescription)
public static ApiContext Create(ApiEnvironmentType environmentType, string apiKey, string deviceDescription,
string proxy=null)
{
return Create(environmentType, apiKey, deviceDescription, new List<string>());
return Create(environmentType, apiKey, deviceDescription, new List<string>(), proxy);
}

/// <summary>
/// Create and initialize an API Context.
/// </summary>
public static ApiContext Create(ApiEnvironmentType environmentType, string apiKey, string deviceDescription,
IList<string> permittedIps)
IList<string> permittedIps, string proxy=null)
{
var apiContext = new ApiContext
{
ApiKey = apiKey,
EnvironmentType = environmentType,
Proxy = proxy,
};
apiContext.Initialize(deviceDescription, permittedIps);

Expand Down Expand Up @@ -201,14 +206,22 @@ public void Save(string fileName)
{
try
{
File.WriteAllText(fileName, BunqJsonConvert.SerializeObject(this), ENCODING_BUNQ_CONF);
File.WriteAllText(fileName, ToJson(), ENCODING_BUNQ_CONF);
}
catch (IOException exception)
{
throw new BunqException(ERROR_COULD_NOT_SAVE_API_CONTEXT, exception);
}
}

/// <summary>
/// Serialize the API Context to JSON.
/// </summary>
public string ToJson()
{
return BunqJsonConvert.SerializeObject(this);
}

/// <summary>
/// Restores a context from a default location.
/// </summary>
Expand All @@ -224,16 +237,22 @@ public static ApiContext Restore(string fileName)
{
try
{
var apiContextJson = File.ReadAllText(fileName, ENCODING_BUNQ_CONF);

return BunqJsonConvert.DeserializeObject<ApiContext>(apiContextJson);
return FromJson(File.ReadAllText(fileName, ENCODING_BUNQ_CONF));
}
catch (IOException exception)
{
throw new BunqException(ERROR_COULD_NOT_RESTORE_API_CONTEXT, exception);
}
}

/// <summary>
/// De-serializes a context from JSON.
/// </summary>
public static ApiContext FromJson(string json)
{
return BunqJsonConvert.DeserializeObject<ApiContext>(json);
}

/// <summary>
/// Returns the base URI of the environment assigned to the API context.
/// </summary>
Expand Down
47 changes: 29 additions & 18 deletions BunqSdk/Http/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class ApiClient
/// Values for the default headers
/// </summary>
private const string CACHE_CONTROL_NONE = "no-cache";
private const string USER_AGENT_BUNQ = "bunq-sdk-csharp/0.9.2.0-beta";
private const string USER_AGENT_BUNQ = "bunq-sdk-csharp/0.10.0.0-beta";
private const string LANGUAGE_EN_US = "en_US";
private const string REGION_NL_NL = "nl_NL";
private const string GEOLOCATION_ZERO = "0 0 0 0 NL";
Expand All @@ -49,15 +49,42 @@ public class ApiClient
/// </summary>
private const string DELIMITER_HEADER_VALUE = ",";

private static HttpClient client;
private readonly HttpClient client;

private readonly ApiContext apiContext;

public ApiClient(ApiContext apiContext)
{
this.apiContext = apiContext;
client = CreateHttpClient();
}

private HttpClient CreateHttpClient()
{
return new HttpClient(CreateHttpClientHandler())
{
BaseAddress = new Uri(apiContext.GetBaseUri())
};
}

private HttpClientHandler CreateHttpClientHandler()
{
// TODO: Add HTTP Public Key Pinning. It is needed to prevent possible man-in-the-middle attacks using
// the fake (or mis-issued) certificates.
// More info: https://timtaubert.de/blog/2014/10/http-public-key-pinning-explained/
// Simply put, we reduce the amount of certificates which are accepted in bunq API responses.
var handler = new HttpClientHandler();

if (apiContext.Proxy != null)
{
handler.Proxy = new BunqProxy(apiContext.Proxy);
handler.UseProxy = true;
}

return handler;
}


/// <summary>
/// Executes a POST request and returns the resulting HTTP response message.
/// </summary>
Expand Down Expand Up @@ -90,7 +117,6 @@ private BunqResponseRaw SendRequest(HttpRequestMessage requestMessage,
SetDefaultHeaders(requestMessage);
SetHeaders(requestMessage, customHeaders);
SetSessionHeaders(requestMessage);
InitializeHttpClientIfNeeded(apiContext);
var responseMessage = client.SendAsync(requestMessage).Result;
AssertResponseSuccess(responseMessage);
ValidateResponse(responseMessage);
Expand Down Expand Up @@ -194,21 +220,6 @@ private string GenerateSignature(HttpRequestMessage requestMessage)
return SecurityUtils.GenerateSignature(requestMessage, apiContext.InstallationContext.KeyPairClient);
}

private static void InitializeHttpClientIfNeeded(ApiContext apiContext)
{
if (client == null)
{
// TODO: Add HTTP Public Key Pinning. It is needed to prevent possible man-in-the-middle attacks using
// the fake (or mis-issued) certificates.
// More info: https://timtaubert.de/blog/2014/10/http-public-key-pinning-explained/
// Simply put, we reduce the amount of certificates which are accepted in bunq API responses.
client = new HttpClient
{
BaseAddress = new Uri(apiContext.GetBaseUri())
};
}
}

private static void AssertResponseSuccess(HttpResponseMessage responseMessage)
{
if (responseMessage.IsSuccessStatusCode) return;
Expand Down
32 changes: 32 additions & 0 deletions BunqSdk/Http/BunqProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Net;

namespace Bunq.Sdk.Http
{
public class BunqProxy : IWebProxy
{
public BunqProxy(string proxyUri)
: this(new Uri(proxyUri))
{
}

public BunqProxy(Uri proxyUri)
{
ProxyUri = proxyUri;
}

public Uri ProxyUri { get; set; }

public ICredentials Credentials { get; set; }

public Uri GetProxy(Uri destination)
{
return ProxyUri;
}

public bool IsBypassed(Uri host)
{
return false; /* Proxy all requests */
}
}
}
13 changes: 9 additions & 4 deletions BunqSdk/Json/InstallationConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,19 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
JsonSerializer serializer)
{
var jObjects = JArray.Load(reader).ToObject<List<JObject>>();
var id = jObjects[INDEX_ID].GetValue(FIELD_ID).ToObject<Id>();
var token = jObjects[INDEX_TOKEN].GetValue(FIELD_TOKEN).ToObject<SessionToken>();
var publicKeyServer = jObjects[INDEX_SERVER_PUBLIC_KEY].GetValue(FIELD_SERVER_PUBLIC_KEY)
.ToObject<PublicKeyServer>();
var id = FetchObject<Id>(jObjects[INDEX_ID], FIELD_ID);
var token = FetchObject<SessionToken>(jObjects[INDEX_TOKEN], FIELD_TOKEN);
var publicKeyServer =
FetchObject<PublicKeyServer>(jObjects[INDEX_SERVER_PUBLIC_KEY], FIELD_SERVER_PUBLIC_KEY);

return new Installation(id, token, publicKeyServer);
}

private static T FetchObject<T>(JToken jToken, string fieldName)
{
return jToken[fieldName].ToObject<T>();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
Expand Down
7 changes: 6 additions & 1 deletion BunqSdk/Model/BunqModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public abstract class BunqModel
private const string FIELD_ID = "Id";
private const string FIELD_UUID = "Uuid";

/// <summary>
/// Index of the very first item in an array.
/// </summary>
private const int INDEX_FIRST = 0;

/// <summary>
/// De-serializes an object from a JSON format specific to Installation and SessionServer.
/// </summary>
Expand Down Expand Up @@ -46,7 +51,7 @@ private static JObject GetResponseContent(BunqResponseRaw responseRaw)
var json = Encoding.UTF8.GetString(responseRaw.BodyBytes);
var responseWithWrapper = BunqJsonConvert.DeserializeObject<JObject>(json);

return responseWithWrapper.GetValue(FIELD_RESPONSE).ToObject<JArray>().Value<JObject>(0);
return responseWithWrapper.GetValue(FIELD_RESPONSE).ToObject<JArray>().Value<JObject>(INDEX_FIRST);
}

private static string GetWrappedContentString(JObject json, string wrapper)
Expand Down
2 changes: 1 addition & 1 deletion BunqSdk/Model/Generated/Card.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public class Card : BunqModel
/// Array of Types, PINs, account IDs assigned to the card.
/// </summary>
[JsonProperty(PropertyName = "pin_code_assignment")]
public CardPinAssignment PinCodeAssignment { get; private set; }
public List<CardPinAssignment> PinCodeAssignment { get; private set; }

public static BunqResponse<Card> Update(ApiContext apiContext, IDictionary<string, object> requestMap,
int userId, int cardId)
Expand Down

0 comments on commit 325af4c

Please sign in to comment.