Skip to content

Commit 5a222d6

Browse files
author
Oleksandr Poliakov
committed
Apply authmechanism renaming and add azure environment.
1 parent c0b7345 commit 5a222d6

File tree

12 files changed

+239
-134
lines changed

12 files changed

+239
-134
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.IO;
18+
using System.Net;
19+
using System.Threading;
20+
using System.Threading.Tasks;
21+
using MongoDB.Bson.IO;
22+
using MongoDB.Bson.Serialization;
23+
using MongoDB.Bson.Serialization.Serializers;
24+
25+
namespace MongoDB.Driver.Core.Authentication.Oidc
26+
{
27+
internal sealed class AzureOidcCallback : IOidcCallback
28+
{
29+
private readonly string _tokenResource;
30+
31+
public AzureOidcCallback(string tokenResource)
32+
{
33+
_tokenResource = tokenResource;
34+
}
35+
36+
public OidcAccessToken GetOidcAccessToken(OidcCallbackParameters parameters, CancellationToken cancellationToken)
37+
{
38+
var request = CreateMetadataRequest(parameters);
39+
using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
40+
{
41+
var response = request.GetResponse();
42+
return ParseMetadataResponse((HttpWebResponse)response);
43+
}
44+
}
45+
46+
public async Task<OidcAccessToken> GetOidcAccessTokenAsync(OidcCallbackParameters parameters, CancellationToken cancellationToken)
47+
{
48+
var request = CreateMetadataRequest(parameters);
49+
using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
50+
{
51+
var response = await request.GetResponseAsync().ConfigureAwait(false);
52+
return ParseMetadataResponse((HttpWebResponse)response);
53+
}
54+
}
55+
56+
private HttpWebRequest CreateMetadataRequest(OidcCallbackParameters parameters)
57+
{
58+
var metadataUrl = $"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource={_tokenResource}";
59+
if (!string.IsNullOrEmpty(parameters.UserName))
60+
{
61+
metadataUrl += $"&client_id={parameters.UserName}";
62+
}
63+
64+
var request = WebRequest.CreateHttp(new Uri(metadataUrl));
65+
request.Headers["Metadata"] = "true";
66+
request.Accept = "application/json";
67+
request.Method = "GET";
68+
69+
return request;
70+
}
71+
72+
private OidcAccessToken ParseMetadataResponse(HttpWebResponse response)
73+
{
74+
if (response.StatusCode != HttpStatusCode.OK)
75+
{
76+
throw new InvalidOperationException($"Response status code does not indicate success {response.StatusCode}:{response.StatusDescription}");
77+
}
78+
79+
using var responseReader = new StreamReader(response.GetResponseStream());
80+
using var jsonReader = new JsonReader(responseReader);
81+
82+
var context = BsonDeserializationContext.CreateRoot(jsonReader);
83+
var document = BsonDocumentSerializer.Instance.Deserialize(context);
84+
85+
var accessToken = document.GetValue("access_token");
86+
return new OidcAccessToken(accessToken.AsString, null);
87+
}
88+
}
89+
}

src/MongoDB.Driver.Core/Core/Authentication/Oidc/IOidcCallback.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,22 @@ public sealed class OidcCallbackParameters
2828
/// Initializes a new instance of the <see cref="OidcCallbackParameters" /> class.
2929
/// </summary>
3030
/// <param name="version">Callback API version number.</param>
31-
public OidcCallbackParameters(int version)
31+
/// <param name="userName">User name.</param>
32+
public OidcCallbackParameters(int version, string userName)
3233
{
3334
Version = version;
35+
UserName = userName;
3436
}
3537

3638
/// <summary>
3739
/// Callback API version number.
3840
/// </summary>
3941
public int Version { get; }
42+
43+
/// <summary>
44+
/// User name.
45+
/// </summary>
46+
public string UserName { get; }
4047
}
4148

4249
/// <summary>

src/MongoDB.Driver.Core/Core/Authentication/Oidc/MongoOidcAuthenticator.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ namespace MongoDB.Driver.Core.Authentication.Oidc
2727
internal sealed class MongoOidcAuthenticator : SaslAuthenticator
2828
{
2929
#region static
30-
public const string CallbackMechanismPropertyName = "OIDC_CALLBACK";
3130
public const string MechanismName = "MONGODB-OIDC";
32-
public const string ProviderMechanismPropertyName = "PROVIDER_NAME";
3331

3432
public static MongoOidcAuthenticator CreateAuthenticator(
3533
string source,
@@ -43,30 +41,27 @@ public static MongoOidcAuthenticator CreateAuthenticator(
4341
properties,
4442
endPoints,
4543
serverApi,
46-
OidcCallbackAdapterCachingFactory.Instance,
47-
OidcKnownCallbackProviders.Instance);
44+
OidcCallbackAdapterCachingFactory.Instance);
4845

4946
public static MongoOidcAuthenticator CreateAuthenticator(
5047
string source,
5148
string principalName,
5249
IEnumerable<KeyValuePair<string, object>> properties,
5350
IReadOnlyList<EndPoint> endPoints,
5451
ServerApi serverApi,
55-
IOidcCallbackAdapterFactory callbackAdapterFactory,
56-
IOidcKnownCallbackProviders oidcKnownCallbackProviders)
52+
IOidcCallbackAdapterFactory callbackAdapterFactory)
5753
{
5854
Ensure.IsNotNull(endPoints, nameof(endPoints));
5955
Ensure.IsNotNull(callbackAdapterFactory, nameof(callbackAdapterFactory));
60-
Ensure.IsNotNull(oidcKnownCallbackProviders, nameof(oidcKnownCallbackProviders));
6156

6257
if (source != "$external")
6358
{
6459
throw new ArgumentException("MONGODB-OIDC authentication must use the $external authentication source.", nameof(source));
6560
}
6661

67-
var configuration = new OidcConfiguration(endPoints, principalName, properties, oidcKnownCallbackProviders);
62+
var configuration = new OidcConfiguration(endPoints, principalName, properties);
6863
var callbackAdapter = callbackAdapterFactory.Get(configuration);
69-
var mechanism = new OidcSaslMechanism(callbackAdapter);
64+
var mechanism = new OidcSaslMechanism(callbackAdapter, principalName);
7065
return new MongoOidcAuthenticator(mechanism, serverApi, configuration);
7166
}
7267
#endregion

src/MongoDB.Driver.Core/Core/Authentication/Oidc/OidcCallbackAdapterFactory.cs

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

16+
using System;
1617
using System.Collections.Concurrent;
1718
using MongoDB.Driver.Core.Misc;
1819

@@ -25,20 +26,39 @@ internal interface IOidcCallbackAdapterFactory
2526

2627
internal class OidcCallbackAdapterCachingFactory : IOidcCallbackAdapterFactory
2728
{
28-
public static readonly OidcCallbackAdapterCachingFactory Instance = new(SystemClock.Instance);
29+
public static readonly OidcCallbackAdapterCachingFactory Instance = new(SystemClock.Instance, EnvironmentVariableProvider.Instance);
2930

3031
private readonly IClock _clock;
32+
private readonly IEnvironmentVariableProvider _environmentVariableProvider;
3133
private readonly ConcurrentDictionary<OidcConfiguration, IOidcCallbackAdapter> _cache = new();
3234

33-
public OidcCallbackAdapterCachingFactory(IClock clock)
35+
public OidcCallbackAdapterCachingFactory(IClock clock, IEnvironmentVariableProvider environmentVariableProvider)
3436
{
3537
_clock = clock;
38+
_environmentVariableProvider = environmentVariableProvider;
3639
}
3740

3841
public IOidcCallbackAdapter Get(OidcConfiguration configuration)
39-
=> _cache.GetOrAdd(configuration, config => new OidcCallbackAdapter(config.Callback, _clock));
42+
=> _cache.GetOrAdd(configuration, CreateCallbackAdapter);
4043

4144
internal void Reset()
4245
=> _cache.Clear();
46+
47+
private IOidcCallbackAdapter CreateCallbackAdapter(OidcConfiguration configuration)
48+
{
49+
var callback = configuration.Callback;
50+
51+
if (!string.IsNullOrEmpty(configuration.Environment))
52+
{
53+
callback = configuration.Environment switch
54+
{
55+
"test" => FileOidcCallback.CreateFromEnvironmentVariable("OIDC_TOKEN_FILE ", _environmentVariableProvider),
56+
"azure" => new AzureOidcCallback(configuration.TokenResource),
57+
_ => throw new NotSupportedException($"Non supported {OidcConfiguration.EnvironmentMechanismPropertyName} value: {configuration.Environment}")
58+
};
59+
}
60+
61+
return new OidcCallbackAdapter(callback, _clock);
62+
}
4363
}
4464
}

src/MongoDB.Driver.Core/Core/Authentication/Oidc/OidcConfiguration.cs

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ namespace MongoDB.Driver.Core.Authentication.Oidc
2424
{
2525
internal sealed class OidcConfiguration
2626
{
27+
public const string CallbackMechanismPropertyName = "OIDC_CALLBACK";
28+
public const string EnvironmentMechanismPropertyName = "ENVIRONMENT";
29+
public const string TokenResourceMechanismPropertyName = "TOKEN_RESOURCE";
30+
31+
private static readonly ISet<string> __supportedEnvironments = new HashSet<string> { "test", "azure" };
2732
private readonly int _hashCode;
2833

2934
public OidcConfiguration(
3035
IEnumerable<EndPoint> endPoints,
3136
string principalName,
32-
IEnumerable<KeyValuePair<string, object>> authMechanismProperties,
33-
IOidcKnownCallbackProviders oidcKnownCallbackProviders)
37+
IEnumerable<KeyValuePair<string, object>> authMechanismProperties)
3438
{
3539
EndPoints = Ensure.IsNotNullOrEmpty(endPoints, nameof(endPoints));
3640
Ensure.IsNotNull(authMechanismProperties, nameof(authMechanismProperties));
@@ -42,15 +46,14 @@ public OidcConfiguration(
4246
{
4347
switch (authorizationProperty.Key)
4448
{
45-
case MongoOidcAuthenticator.CallbackMechanismPropertyName:
46-
{
47-
Callback = GetProperty<IOidcCallback>(authorizationProperty);
48-
}
49+
case CallbackMechanismPropertyName:
50+
Callback = GetProperty<IOidcCallback>(authorizationProperty);
51+
break;
52+
case EnvironmentMechanismPropertyName:
53+
Environment = GetProperty<string>(authorizationProperty);
4954
break;
50-
case MongoOidcAuthenticator.ProviderMechanismPropertyName:
51-
{
52-
ProviderName = GetProperty<string>(authorizationProperty);
53-
}
55+
case TokenResourceMechanismPropertyName:
56+
TokenResource = GetProperty<string>(authorizationProperty);
5457
break;
5558
default:
5659
throw new ArgumentException(
@@ -63,17 +66,6 @@ public OidcConfiguration(
6366
ValidateOptions();
6467
_hashCode = CalculateHashCode();
6568

66-
if (ProviderName != null)
67-
{
68-
Callback = ProviderName switch
69-
{
70-
"aws" => oidcKnownCallbackProviders.Aws,
71-
_ => throw new ArgumentException(
72-
$"Not supported value of {MongoOidcAuthenticator.ProviderMechanismPropertyName} mechanism property: {ProviderName}.",
73-
MongoOidcAuthenticator.ProviderMechanismPropertyName)
74-
};
75-
}
76-
7769
static T GetProperty<T>(KeyValuePair<string, object> property)
7870
{
7971
if (property.Value is T result)
@@ -85,10 +77,11 @@ static T GetProperty<T>(KeyValuePair<string, object> property)
8577
}
8678
}
8779

80+
public IOidcCallback Callback { get; }
8881
public IEnumerable<EndPoint> EndPoints { get; }
82+
public string Environment { get; }
8983
public string PrincipalName { get; }
90-
public string ProviderName { get; }
91-
public IOidcCallback Callback { get; }
84+
public string TokenResource { get; }
9285

9386
public override int GetHashCode() => _hashCode;
9487

@@ -98,29 +91,52 @@ public override bool Equals(object obj)
9891
if (object.ReferenceEquals(this, obj)) { return true; }
9992
return
10093
obj is OidcConfiguration other &&
101-
ProviderName == other.ProviderName &&
94+
Environment == other.Environment &&
10295
PrincipalName == other.PrincipalName &&
10396
object.Equals(Callback, other.Callback) &&
97+
TokenResource == other.TokenResource &&
10498
EndPoints.SequenceEqual(other.EndPoints, EndPointHelper.EndPointEqualityComparer);
10599
}
106100

107101
private int CalculateHashCode()
108102
=> new Hasher()
109-
.Hash(ProviderName)
103+
.Hash(Environment)
110104
.Hash(PrincipalName)
111105
.HashElements(EndPoints)
106+
.Hash(TokenResource)
112107
.GetHashCode();
113108

114109
private void ValidateOptions()
115110
{
116-
if (ProviderName == null && Callback == null)
111+
if (Environment == null && Callback == null)
112+
{
113+
throw new ArgumentException($"{EnvironmentMechanismPropertyName} or {CallbackMechanismPropertyName} must be configured.");
114+
}
115+
116+
if (Environment != null && Callback != null)
117+
{
118+
throw new ArgumentException($"{CallbackMechanismPropertyName} is mutually exclusive with {EnvironmentMechanismPropertyName}.");
119+
}
120+
121+
if (Environment != null && !__supportedEnvironments.Contains(Environment))
122+
{
123+
throw new ArgumentException(
124+
$"Not supported value of {EnvironmentMechanismPropertyName} mechanism property: {Environment}.",
125+
EnvironmentMechanismPropertyName);
126+
}
127+
128+
if (!string.IsNullOrEmpty(TokenResource) && Environment != "azure")
117129
{
118-
throw new ArgumentException($"{MongoOidcAuthenticator.ProviderMechanismPropertyName} or {MongoOidcAuthenticator.CallbackMechanismPropertyName} must be configured.");
130+
throw new ArgumentException(
131+
$"{TokenResourceMechanismPropertyName} mechanism property supported only by azure environment.",
132+
TokenResourceMechanismPropertyName);
119133
}
120134

121-
if (ProviderName != null && Callback != null)
135+
if (Environment == "azure" && string.IsNullOrEmpty(TokenResource))
122136
{
123-
throw new ArgumentException($"{MongoOidcAuthenticator.CallbackMechanismPropertyName} is mutually exclusive with {MongoOidcAuthenticator.ProviderMechanismPropertyName}.");
137+
throw new ArgumentException(
138+
$"{TokenResourceMechanismPropertyName} mechanism property is required by azure environment.",
139+
TokenResourceMechanismPropertyName);
124140
}
125141
}
126142
}

src/MongoDB.Driver.Core/Core/Authentication/Oidc/OidcKnownCallbackProviders.cs

Lines changed: 0 additions & 40 deletions
This file was deleted.

0 commit comments

Comments
 (0)