Skip to content

Commit

Permalink
Merge pull request #1520 from stripe/ob-validate-api-key
Browse files Browse the repository at this point in the history
API key validation
  • Loading branch information
ob-stripe authored Feb 22, 2019
2 parents 31d32e0 + fb63eb1 commit cec3d24
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 13 deletions.
10 changes: 2 additions & 8 deletions src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ public static string ApiKey
return apiKey;
}

set
{
apiKey = value;
}
set => apiKey = value;
}

/// <summary>Gets or sets the base URL for Stripe's OAuth API.</summary>
Expand Down Expand Up @@ -126,10 +123,7 @@ public static IStripeClient StripeClient
return stripeClient;
}

set
{
stripeClient = value;
}
set => stripeClient = value;
}

/// <summary>Gets the version of the Stripe.net client library.</summary>
Expand Down
32 changes: 29 additions & 3 deletions src/Stripe.net/Infrastructure/Public/StripeRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Stripe
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Stripe.Infrastructure;
using Stripe.Infrastructure.FormEncoding;

/// <summary>
Expand All @@ -31,9 +32,7 @@ public StripeRequest(

this.Uri = BuildUri(method, path, options, requestOptions);

this.AuthorizationHeader = new AuthenticationHeaderValue(
"Bearer",
requestOptions?.ApiKey ?? StripeConfiguration.ApiKey);
this.AuthorizationHeader = BuildAuthorizationHeader(requestOptions);

this.StripeHeaders = BuildStripeHeaders(method, requestOptions);
}
Expand Down Expand Up @@ -99,6 +98,33 @@ private static Uri BuildUri(
return new Uri(b.ToString());
}

private static AuthenticationHeaderValue BuildAuthorizationHeader(
RequestOptions requestOptions)
{
string apiKey = requestOptions?.ApiKey ?? StripeConfiguration.ApiKey;

if (string.IsNullOrEmpty(apiKey))
{
var message = "No API key provided. Set your API key using "
+ "`StripeConfiguration.ApiKey = \"<API-KEY>\"`. You can generate API keys "
+ "from the Stripe Dashboard. See "
+ "https://stripe.com/docs/api/authentication for details or contact support "
+ "at https://support.stripe.com/email if you have any questions.";
throw new StripeException(message);
}

if (StringUtils.ContainsWhitespace(apiKey))
{
var message = "Your API key is invalid, as it contains whitespace. You can "
+ "double-check your API key from the Stripe Dashboard. See "
+ "https://stripe.com/docs/api/authentication for details or contact support "
+ "at https://support.stripe.com/email if you have any questions.";
throw new StripeException(message);
}

return new AuthenticationHeaderValue("Bearer", apiKey);
}

private static Dictionary<string, string> BuildStripeHeaders(
HttpMethod method,
RequestOptions requestOptions)
Expand Down
18 changes: 18 additions & 0 deletions src/Stripe.net/Infrastructure/StringUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace Stripe.Infrastructure

internal static class StringUtils
{
private static Regex whitespaceRegex = new Regex(@"\s", RegexOptions.CultureInvariant);

/// <summary>Converts the string to snake case.</summary>
/// <param name="str">The string to convert.</param>
/// <returns>A string with the contents of the input string converted to snake_case.</returns>
Expand Down Expand Up @@ -52,5 +54,21 @@ public static bool SecureEquals(string a, string b)

return result == 0;
}

/// <summary>Checks whether a string contains any whitespace characters or not.</summary>
/// <param name="str">The string to check.</param>
/// <returns>
/// <c>true</c> if the string contains any whitespace characters; otherwise, <c>false</c>.
/// </returns>
/// <exception name="ArgumentNullException">Thrown if <c>str</c> is <c>null</c>.</exception>
public static bool ContainsWhitespace(string str)
{
if (str == null)
{
throw new ArgumentNullException(nameof(str));
}

return whitespaceRegex.IsMatch(str);
}
}
}
2 changes: 1 addition & 1 deletion src/Stripe.net/Services/_base/Service.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ protected RequestOptions SetupRequestOptions(RequestOptions requestOptions)
requestOptions = new RequestOptions();
}

if (!string.IsNullOrEmpty(this.ApiKey))
if (string.IsNullOrEmpty(requestOptions.ApiKey) && !string.IsNullOrEmpty(this.ApiKey))
{
requestOptions.ApiKey = this.ApiKey;
}
Expand Down
72 changes: 71 additions & 1 deletion src/StripeTests/Infrastructure/Public/StripeRequestTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ namespace StripeTests
using System.Net.Http;
using System.Threading.Tasks;
using Stripe;
using Stripe.Infrastructure;
using StripeTests.Infrastructure.TestData;
using Xunit;

Expand Down Expand Up @@ -86,5 +85,76 @@ public void Ctor_RequestOptions()
Assert.Equal("acct_456", request.StripeHeaders["Stripe-Account"]);
Assert.Null(request.Content);
}

[Fact]
public void Ctor_ThrowsIfApiKeyIsNull()
{
var origKey = StripeConfiguration.ApiKey;

try
{
StripeConfiguration.ApiKey = null;

var options = new TestOptions();
var requestOptions = new RequestOptions();

var exception = Assert.Throws<StripeException>(() =>
new StripeRequest(HttpMethod.Get, "/get", options, requestOptions));

Assert.Contains("No API key provided.", exception.Message);
}
finally
{
StripeConfiguration.ApiKey = origKey;
}
}

[Fact]
public void Ctor_ThrowsIfApiKeyIsEmpty()
{
var origKey = StripeConfiguration.ApiKey;

try
{
StripeConfiguration.ApiKey = string.Empty;

var options = new TestOptions();
var requestOptions = new RequestOptions();

var exception = Assert.Throws<StripeException>(() =>
new StripeRequest(HttpMethod.Get, "/get", options, requestOptions));

Assert.Contains("No API key provided.", exception.Message);
}
finally
{
StripeConfiguration.ApiKey = origKey;
}
}

[Fact]
public void Ctor_ThrowsIfApiKeyContainsWhitespace()
{
var origKey = StripeConfiguration.ApiKey;

try
{
StripeConfiguration.ApiKey = "sk_test_123\n";

var options = new TestOptions();
var requestOptions = new RequestOptions();

var exception = Assert.Throws<StripeException>(() =>
new StripeRequest(HttpMethod.Get, "/get", options, requestOptions));

Assert.Contains(
"Your API key is invalid, as it contains whitespace.",
exception.Message);
}
finally
{
StripeConfiguration.ApiKey = origKey;
}
}
}
}
24 changes: 24 additions & 0 deletions src/StripeTests/Infrastructure/StringUtilsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,29 @@ public void SecureEquals()
StringUtils.SecureEquals(testCase.data.a, testCase.data.b));
}
}

[Fact]
public void ContainsWhitespace()
{
var testCases = new[]
{
new { data = "sk_test_123", want = false },
new { data = "sk_test_4eC39HqLyjWDarjtT1zdp7dc", want = false },
new { data = "abc", want = false },
new { data = "sk-test-123", want = false },
new { data = string.Empty, want = false },
new { data = "sk_test_123\n", want = true },
new { data = "\nsk_test_123", want = true },
new { data = "sk_test_\n123", want = true },
new { data = "sk_test_123 ", want = true },
new { data = " sk_test_123", want = true },
new { data = "sk_test_ 123", want = true },
};

foreach (var testCase in testCases)
{
Assert.Equal(testCase.want, StringUtils.ContainsWhitespace(testCase.data));
}
}
}
}

0 comments on commit cec3d24

Please sign in to comment.