Skip to content

Commit dc8db54

Browse files
fix(Spanner.Data): Specify credential type when loading from file.
1 parent f6be971 commit dc8db54

5 files changed

Lines changed: 85 additions & 21 deletions

File tree

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerClientCreationOptionsTest.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,22 @@ public async Task CredentialFileRelative()
131131
Assert.NotNull(await options.CreateSpannerClientAsync(new Spanner.V1.SpannerSettings()));
132132
}
133133

134+
[Fact]
135+
public async Task CredentialFile_WithWrongCredentialType_Fails()
136+
{
137+
var builder = new SpannerConnectionStringBuilder("CredentialFile=SpannerEF-8dfc036f6000.json;CredentialType=authorized_user");
138+
var options = new SpannerClientCreationOptions(builder);
139+
await Assert.ThrowsAsync<InvalidOperationException>(() => options.CreateSpannerClientAsync(new Spanner.V1.SpannerSettings()));
140+
}
141+
142+
[Fact]
143+
public async Task CredentialFile_WithCorrectCredentialType_Succeeds()
144+
{
145+
var builder = new SpannerConnectionStringBuilder("CredentialFile=SpannerEF-8dfc036f6000.json;CredentialType=service_account");
146+
var options = new SpannerClientCreationOptions(builder);
147+
Assert.NotNull(await options.CreateSpannerClientAsync(new Spanner.V1.SpannerSettings()));
148+
}
149+
134150
[Fact]
135151
public async Task CredentialFileP12Error()
136152
{

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data.Tests/SpannerConnectionStringBuilderTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,5 +373,33 @@ public void IsolationLevelIsConvertedToEnum()
373373

374374
Assert.Equal(System.Data.IsolationLevel.RepeatableRead, builder.IsolationLevel);
375375
}
376+
377+
[Fact]
378+
public void CredentialType_Default()
379+
{
380+
var builder = new SpannerConnectionStringBuilder();
381+
Assert.Equal(JsonCredentialParameters.ServiceAccountCredentialType, builder.CredentialType);
382+
}
383+
384+
[Fact]
385+
public void CredentialType_Explicit()
386+
{
387+
var builder = new SpannerConnectionStringBuilder("CredentialType=authorized_user");
388+
Assert.Equal("authorized_user", builder.CredentialType);
389+
builder.CredentialType = "service_account";
390+
Assert.Equal("service_account", builder.CredentialType);
391+
// DbConnectionStringBuilder lower-cases keywords, annoyingly.
392+
Assert.Equal("credentialtype=service_account", builder.ToString());
393+
}
394+
395+
[Fact]
396+
public void CredentialType_NullSet_RestoresDefault()
397+
{
398+
var builder = new SpannerConnectionStringBuilder();
399+
builder.CredentialType = "authorized_user";
400+
Assert.Equal("authorized_user", builder.CredentialType);
401+
builder.CredentialType = null;
402+
Assert.Equal(JsonCredentialParameters.ServiceAccountCredentialType, builder.CredentialType);
403+
}
376404
}
377405
}

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerClientCreationOptions.cs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using Google.Api.Gax.Grpc.Gcp;
16+
using Google.Apis.Auth.OAuth2;
1617
using Google.Cloud.Spanner.Admin.Database.V1;
1718
using Google.Cloud.Spanner.V1;
1819
using System;
@@ -32,16 +33,16 @@ internal sealed class SpannerClientCreationOptions : IEquatable<SpannerClientCre
3233

3334
internal bool UsesEmulator { get; }
3435

36+
private readonly string _credentialFile;
37+
private readonly string _credentialType;
38+
3539
internal SpannerClientCreationOptions(SpannerConnectionStringBuilder builder)
3640
{
3741
var clientBuilder = new SpannerClientBuilder
3842
{
3943
EmulatorDetection = builder.EmulatorDetection,
4044
EnvironmentVariableProvider = builder.EnvironmentVariableProvider,
4145
Endpoint = builder.ContainsKey(nameof(builder.Host)) || builder.ContainsKey(nameof(builder.Port)) ? builder.EndPoint : null,
42-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
43-
CredentialsPath = builder.CredentialFile == "" ? null: builder.CredentialFile,
44-
#pragma warning restore CS0618
4546
ChannelCredentials = builder.CredentialOverride,
4647
GoogleCredential = builder.GoogleCredential,
4748
AffinityChannelPoolConfiguration = new ChannelPoolConfig
@@ -59,6 +60,8 @@ internal SpannerClientCreationOptions(SpannerConnectionStringBuilder builder)
5960

6061
UsesEmulator = emulatorBuilder is not null;
6162
ClientBuilder = emulatorBuilder ?? clientBuilder;
63+
_credentialFile = builder.CredentialFile;
64+
_credentialType = builder.CredentialType;
6265
}
6366

6467
internal Task<SpannerClient> CreateSpannerClientAsync(SpannerSettings settings) =>
@@ -68,11 +71,8 @@ internal Task<SpannerClient> CreateSpannerClientAsync(SpannerSettings settings)
6871
// Note we don't copy emulator detection properties because we already took care
6972
// of emulator detection on the constructor.
7073
Endpoint = ClientBuilder.Endpoint,
71-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
72-
CredentialsPath = ClientBuilder.CredentialsPath,
73-
#pragma warning disable CS0618
7474
ChannelCredentials = ClientBuilder.ChannelCredentials,
75-
GoogleCredential = ClientBuilder.GoogleCredential,
75+
GoogleCredential = GetEffectiveGoogleCredential(),
7676
AffinityChannelPoolConfiguration = ClientBuilder.AffinityChannelPoolConfiguration,
7777
LeaderRoutingEnabled = ClientBuilder.LeaderRoutingEnabled,
7878
DirectedReadOptions = ClientBuilder.DirectedReadOptions,
@@ -88,11 +88,8 @@ internal DatabaseAdminClientBuilder CreateDatabaseAdminClientBuilder()
8888
// Note we don't copy emulator detection properties because we already took care
8989
// of emulator detection on the constructor.
9090
Endpoint = ClientBuilder.Endpoint,
91-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
92-
CredentialsPath = ClientBuilder.CredentialsPath,
93-
#pragma warning restore CS0618
9491
ChannelCredentials = ClientBuilder.ChannelCredentials,
95-
GoogleCredential = ClientBuilder.GoogleCredential,
92+
GoogleCredential = GetEffectiveGoogleCredential(),
9693
// If we ever have settings of our own, we need to merge those with these.
9794
Settings = CreateDatabaseAdminSettings(),
9895
UniverseDomain = ClientBuilder.UniverseDomain,
@@ -113,11 +110,10 @@ other is not null &&
113110
UsesEmulator == other.UsesEmulator &&
114111
// TODO: Consider overriding ClientBuilderBase and SpannerClientBuilder Equals, etc.
115112
Equals(ClientBuilder.Endpoint, other.ClientBuilder.Endpoint) &&
116-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
117-
Equals(ClientBuilder.CredentialsPath, other.ClientBuilder.CredentialsPath) &&
118-
#pragma warning restore CS0618
119113
Equals(ClientBuilder.ChannelCredentials, other.ClientBuilder.ChannelCredentials) &&
120114
Equals(ClientBuilder.GoogleCredential, other.ClientBuilder.GoogleCredential) &&
115+
_credentialFile == other._credentialFile &&
116+
_credentialType == other._credentialType &&
121117
Equals(ClientBuilder.AffinityChannelPoolConfiguration, other.ClientBuilder.AffinityChannelPoolConfiguration) &&
122118
ClientBuilder.LeaderRoutingEnabled == other.ClientBuilder.LeaderRoutingEnabled &&
123119
Equals(ClientBuilder.DirectedReadOptions, other.ClientBuilder.DirectedReadOptions) &&
@@ -129,11 +125,10 @@ public override int GetHashCode()
129125
{
130126
int hash = 31;
131127
hash = hash * 23 + (ClientBuilder.Endpoint?.GetHashCode() ?? 0);
132-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
133-
hash = hash * 23 + (ClientBuilder.CredentialsPath?.GetHashCode() ?? 0);
134-
#pragma warning restore CS0618
135128
hash = hash * 23 + (ClientBuilder.ChannelCredentials?.GetHashCode() ?? 0);
136129
hash = hash * 23 + (ClientBuilder.GoogleCredential?.GetHashCode() ?? 0);
130+
hash = hash * 23 + (_credentialFile?.GetHashCode() ?? 0);
131+
hash = hash * 23 + (_credentialType?.GetHashCode() ?? 0);
137132
hash = hash * 23 + UsesEmulator.GetHashCode();
138133
hash = hash * 23 + (ClientBuilder.AffinityChannelPoolConfiguration?.GetHashCode() ?? 0);
139134
hash = hash * 23 + (ClientBuilder.LeaderRoutingEnabled.GetHashCode());
@@ -150,12 +145,14 @@ public override int GetHashCode()
150145
public override string ToString()
151146
{
152147
var builder = new StringBuilder($"EndPoint: {ClientBuilder.Endpoint ?? "Default"}");
153-
#pragma warning disable CS0618 // Temporarily disable warnings for obsolete methods. See b/453009677 for more details.
154-
if (!string.IsNullOrEmpty(ClientBuilder.CredentialsPath))
148+
if (!string.IsNullOrEmpty(_credentialFile))
149+
{
150+
builder.Append($"; CredentialsFile: {_credentialFile}");
151+
}
152+
if (!string.IsNullOrEmpty(_credentialType))
155153
{
156-
builder.Append($"; CredentialsFile: {ClientBuilder.CredentialsPath}");
154+
builder.Append($"; CredentialType: {_credentialType}");
157155
}
158-
#pragma warning restore CS0618
159156
if (ClientBuilder.ChannelCredentials is not null)
160157
{
161158
builder.Append($"; CredentialsOverride: True");
@@ -172,5 +169,8 @@ public override string ToString()
172169
builder.Append($"; GrpcAdapter: {ClientBuilder.GrpcAdapter?.GetType().Name ?? "None"}");
173170
return builder.ToString();
174171
}
172+
173+
private GoogleCredential GetEffectiveGoogleCredential() =>
174+
ClientBuilder.GoogleCredential ?? (string.IsNullOrEmpty(_credentialFile) ? null : CredentialFactory.FromFile(_credentialFile, _credentialType));
175175
}
176176
}

apis/Google.Cloud.Spanner.Data/Google.Cloud.Spanner.Data/SpannerConnectionStringBuilder.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public sealed class SpannerConnectionStringBuilder : DbConnectionStringBuilder
5151
internal const int DefaultMaxConcurrentStreamsLowWatermark = 20;
5252

5353
private const string CredentialFileKeyword = "CredentialFile";
54+
private const string CredentialTypeKeyword = "CredentialType";
5455
private const string DataSourceKeyword = "Data Source";
5556
private const string UseClrDefaultForNullKeyword = "UseClrDefaultForNull";
5657
private const string EnableGetSchemaTableKeyword = "EnableGetSchemaTable";
@@ -94,6 +95,16 @@ public string CredentialFile
9495
set => this[CredentialFileKeyword] = value;
9596
}
9697

98+
/// <summary>
99+
/// The expected type of the credential provided via <see cref="CredentialFile"/>.
100+
/// Defaults to <see cref="JsonCredentialParameters.ServiceAccountCredentialType"/>.
101+
/// </summary>
102+
public string CredentialType
103+
{
104+
get => GetValueOrDefault(CredentialTypeKeyword, JsonCredentialParameters.ServiceAccountCredentialType);
105+
set => this[CredentialTypeKeyword] = value;
106+
}
107+
97108
/// <summary>
98109
/// Option to change between the default handling of null database values (return <see cref="DBNull.Value">DBNull.Value</see>) or
99110
/// the non-standard handling (return the default value for whatever type is requested).

apis/Google.Cloud.Spanner.Data/docs/connection_string.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ Example:
2525

2626
- CredentialFile=/path/to/credentials.json
2727

28+
## CredentialType
29+
30+
The expected type of the credential provided via `CredentialFile`.
31+
Defaults to `service_account`.
32+
33+
Example:
34+
35+
- CredentialType=authorized_user
36+
2837
## EnableGetSchemaTable
2938

3039
When set to true (and when targeting .NET 4.5 or .NET Standard 2.0;

0 commit comments

Comments
 (0)