Skip to content

Commit d53bbf2

Browse files
committed
[#10] [add] impl
1 parent 569bcea commit d53bbf2

File tree

6 files changed

+248
-24
lines changed

6 files changed

+248
-24
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"dotnet-test-explorer.testProjectPath": "tests/**/*Tests.csproj",
33
"dotnet-test-explorer.testArguments": "/p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage/lcov.info",
44
"cSpell.words": [
5+
"appsettings",
56
"Authpb",
67
"cref",
78
"Etcdserverpb",

src/Etcd.Microsoft.Extensions.Configuration/Auth/Credentials.cs

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,52 @@
33
namespace Etcd.Microsoft.Extensions.Configuration.Auth;
44

55
/// <summary>
6-
/// Provides credentials
6+
/// Provides credentials.
77
/// </summary>
88
/// <seealso cref="ICredentials" />
99
public class Credentials : ICredentials
1010
{
11+
private const string DefaultUserNameEnvironmentVariableName = "ETCD_CLIENT_USER_NAME";
12+
private const string DefaultPasswordEnvironmentVariableName = "ETCD_CLIENT_PASSWORD";
13+
1114
/// <summary>
1215
/// Initializes a new instance of the <see cref="Credentials"/> class.
1316
/// </summary>
1417
/// <param name="userName">Name of the user.</param>
1518
/// <param name="password">The password.</param>
19+
/// <param name="userNameSource">The source of the username.</param>
20+
/// <param name="passwordSource">The source of the password.</param>
21+
/// <param name="information">The information about the credentials.</param>
1622
/// <exception cref="ArgumentException">
1723
/// Value cannot be null or empty. - userName
1824
/// or
1925
/// Value cannot be null or empty. - password
2026
/// </exception>
21-
public Credentials(string userName, string password)
27+
public Credentials(string userName, string password,
28+
CredentialsSource userNameSource = CredentialsSource.Code,
29+
CredentialsSource passwordSource = CredentialsSource.Code,
30+
string? information = null)
2231
{
2332
if (string.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", nameof(userName));
2433
if (string.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", nameof(password));
2534

2635
UserName = userName;
2736
Password = password;
37+
UserNameSource = userNameSource;
38+
PasswordSource = passwordSource;
39+
Information = information;
2840
}
2941

42+
/// <summary>
43+
/// Gets the source of the user name.
44+
/// </summary>
45+
public CredentialsSource UserNameSource { get; }
46+
47+
/// <summary>
48+
/// Gets the source of the password.
49+
/// </summary>
50+
public CredentialsSource PasswordSource { get; }
51+
3052
/// <summary>
3153
/// Gets the name of the user.
3254
/// </summary>
@@ -42,4 +64,158 @@ public Credentials(string userName, string password)
4264
/// The password.
4365
/// </value>
4466
public string Password { get; }
67+
68+
/// <summary>
69+
/// Gets the information about the credentials.
70+
/// </summary>
71+
public string? Information { get; private set; }
72+
73+
/// <summary>
74+
/// Gets the string representation of the credentials.
75+
/// </summary>
76+
override public string ToString() => Information ?? "etcd code based credentials";
77+
78+
/// <summary>
79+
/// Creates a new credentials instance overriding values from environment variables if they are exists.
80+
/// </summary>
81+
/// <param name="userName">The user name.</param>
82+
/// <param name="password">The password.</param>
83+
/// <param name="userNameEnvironmentVariableName">The name of the user name environment variable.</param>
84+
/// <param name="passwordEnvironmentVariableName">The name of the password environment variable.</param>
85+
/// <exception cref="EtcdException">The etcd user name or password are not provided via code and not found in the environment variable `{userNameEnvironmentVariableName}`.</exception>
86+
public static ICredentials WithOverrideFromEnvironmentVariables(
87+
string userName,
88+
string password,
89+
string userNameEnvironmentVariableName = DefaultUserNameEnvironmentVariableName,
90+
string passwordEnvironmentVariableName = DefaultPasswordEnvironmentVariableName)
91+
{
92+
var userNameSource = CredentialsSource.Code;
93+
var passwordSource = CredentialsSource.Code;
94+
95+
var environmentUserName = Environment.GetEnvironmentVariable(userNameEnvironmentVariableName);
96+
var environmentPassword = Environment.GetEnvironmentVariable(passwordEnvironmentVariableName);
97+
98+
if (!string.IsNullOrEmpty(environmentUserName))
99+
{
100+
userName = environmentUserName;
101+
102+
userNameSource = CredentialsSource.EnvironmentVariables;
103+
}
104+
105+
if (string.IsNullOrEmpty(userName))
106+
throw new EtcdException($"Etcd user name is not provided via code and not found in the environment variable `{userNameEnvironmentVariableName}`.");
107+
108+
if (!string.IsNullOrEmpty(environmentPassword))
109+
{
110+
password = environmentPassword;
111+
112+
passwordSource = CredentialsSource.EnvironmentVariables;
113+
}
114+
115+
if (string.IsNullOrEmpty(password))
116+
throw new EtcdException($"Etcd password is not provided via code and not found in the environment variable `{passwordEnvironmentVariableName}`.");
117+
118+
return new Credentials(userName, password, userNameSource, passwordSource,
119+
FormatInformation(
120+
userNameSource,
121+
passwordSource,
122+
userNameEnvironmentVariableName,
123+
passwordEnvironmentVariableName));
124+
}
125+
126+
/// <summary>
127+
/// Creates a new credentials instance overriding values from environment variables if they are exists.
128+
/// </summary>
129+
/// <param name="userName">The user name.</param>
130+
/// <param name="password">The password.</param>
131+
/// <param name="passwordEnvironmentVariableName">The name of the password environment variable.</param>
132+
/// <exception cref="EtcdException">The etcd user name or password are not provided via code and not found in the environment variable `{userNameEnvironmentVariableName}`.</exception>
133+
public static ICredentials WithOverrideFromEnvironmentVariables(
134+
string userName,
135+
string password,
136+
string passwordEnvironmentVariableName = DefaultPasswordEnvironmentVariableName)
137+
{
138+
var userNameSource = CredentialsSource.Code;
139+
var passwordSource = CredentialsSource.Code;
140+
141+
var environmentPassword = Environment.GetEnvironmentVariable(passwordEnvironmentVariableName);
142+
143+
if (string.IsNullOrEmpty(userName))
144+
throw new EtcdException($"Etcd user name is not provided.");
145+
146+
if (!string.IsNullOrEmpty(environmentPassword))
147+
{
148+
password = environmentPassword;
149+
150+
passwordSource = CredentialsSource.EnvironmentVariables;
151+
}
152+
153+
if (string.IsNullOrEmpty(password))
154+
throw new EtcdException($"Etcd password is not provided via code and not found in the environment variable `{passwordEnvironmentVariableName}`.");
155+
156+
return new Credentials(userName, password, userNameSource, passwordSource,
157+
FormatInformation(
158+
userNameSource,
159+
passwordSource,
160+
null,
161+
passwordEnvironmentVariableName));
162+
}
163+
164+
165+
/// <summary>
166+
/// Creates a new credentials instance from environment variables.
167+
/// </summary>
168+
/// <param name="userNameEnvironmentVariableName">The name of the user name environment variable.</param>
169+
/// <param name="passwordEnvironmentVariableName">The name of the password environment variable.</param>
170+
/// <exception cref="EtcdException">The etcd user name or password are not provided via code and not found in the environment variable `{userNameEnvironmentVariableName}`.</exception>
171+
public static ICredentials FromEnvironmentVariables(
172+
string userNameEnvironmentVariableName = DefaultUserNameEnvironmentVariableName,
173+
string passwordEnvironmentVariableName = DefaultPasswordEnvironmentVariableName)
174+
{
175+
var userNameSource = CredentialsSource.EnvironmentVariables;
176+
var passwordSource = CredentialsSource.EnvironmentVariables;
177+
178+
var userName = Environment.GetEnvironmentVariable(userNameEnvironmentVariableName);
179+
var password = Environment.GetEnvironmentVariable(passwordEnvironmentVariableName);
180+
181+
if (string.IsNullOrEmpty(userName))
182+
throw new EtcdException($"Etcd user name is not found in the environment variable `{userNameEnvironmentVariableName}`.");
183+
184+
if (string.IsNullOrEmpty(password))
185+
throw new EtcdException($"Etcd password is not found in the environment variable `{passwordEnvironmentVariableName}`.");
186+
187+
return new Credentials(userName, password, userNameSource, passwordSource,
188+
FormatInformation(
189+
userNameSource,
190+
passwordSource,
191+
userNameEnvironmentVariableName,
192+
passwordEnvironmentVariableName));
193+
}
194+
195+
private static string FormatInformation(
196+
CredentialsSource userNameSource,
197+
CredentialsSource passwordSource,
198+
string? userNameEnvironmentVariableName = null,
199+
string? passwordEnvironmentVariableName = null) =>
200+
FormatUserNameInformation(userNameSource, userNameEnvironmentVariableName) + ", " + FormatPassword(passwordSource, passwordEnvironmentVariableName);
201+
202+
private static string FormatUserNameInformation(CredentialsSource userNameSource, string? userNameEnvironmentVariableName = null)
203+
{
204+
var result = $"etcd user name source: {userNameSource}";
205+
206+
if (userNameSource == CredentialsSource.EnvironmentVariables)
207+
result += $" ({userNameEnvironmentVariableName})";
208+
209+
return result;
210+
}
211+
212+
private static string FormatPassword(CredentialsSource passwordSource, string? passwordEnvironmentVariableName = null)
213+
{
214+
var result = $"etcd password source: {passwordSource}";
215+
216+
if (passwordSource == CredentialsSource.EnvironmentVariables)
217+
result += $" ({passwordEnvironmentVariableName})";
218+
219+
return result;
220+
}
45221
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Etcd.Microsoft.Extensions.Configuration.Auth
2+
{
3+
/// <summary>
4+
/// Possible sources for credentials.
5+
/// </summary>
6+
public enum CredentialsSource
7+
{
8+
/// <summary>
9+
/// Credentials provided via code.
10+
/// </summary>
11+
Code = 0,
12+
/// <summary>
13+
/// Credentials provided via environment variables.
14+
/// </summary>
15+
EnvironmentVariables = 1
16+
}
17+
}

src/Etcd.Microsoft.Extensions.Configuration/Client/EtcdKeyValueClient.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,9 @@ private IEnumerable<Permission> GetPermissions(string role) =>
171171
}, GetMetadata()).Perm;
172172

173173
private Metadata GetMetadata() =>
174-
new()
175-
{
176-
new("token", _token)
177-
};
174+
[
175+
new("token", _token!)
176+
];
178177

179178
private void CheckIsAuthenticated()
180179
{

src/Etcd.Microsoft.Extensions.Configuration/Etcd.Microsoft.Extensions.Configuration.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
</PropertyGroup>
2525
<ItemGroup>
2626
<PackageReference Include="dotnet-etcd" Version="4.2.0" />
27-
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
27+
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.2" />
2828
</ItemGroup>
2929
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
30-
<PackageReference Include="Grpc.Core" Version="2.46.3" />
30+
<PackageReference Include="Grpc.Core" Version="2.46.6" />
3131
</ItemGroup>
3232
<ItemGroup>
3333
<None Include="..\..\images\icon.png" Pack="true" Visible="false" PackagePath="" />

tests/Integration/Etcd.Microsoft.Extensions.Configuration.IntegrationTests.Core/ConfigurationBuilderTests.cs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Etcd.Microsoft.Extensions.Configuration.Auth;
55
using Etcd.Microsoft.Extensions.Configuration.Settings;
66
using NUnit.Framework;
7+
using System;
78

89
namespace Etcd.Microsoft.Extensions.Configuration.IntegrationTests.Core;
910

@@ -16,33 +17,63 @@ public void Build_WithSettingsFromEtcd_ValuesLoaded()
1617
{
1718
// Arrange
1819

20+
var credentials = new Credentials("MyUserName", "passw");
21+
var etcdSettings = new EtcdSettings("http://localhost:2379");
22+
1923
var config = new ConfigurationBuilder()
20-
.AddEtcd(
21-
new Credentials("MyUserName", "passw"),
22-
new EtcdSettings("https://serveraddress:2379"),
23-
"MyPrefix")
24+
.AddEtcd(credentials, etcdSettings)
25+
.AddEtcd(credentials, etcdSettings, "MyPrefix")
2426
.Build();
2527

2628
// Act
29+
PerformTest(config);
30+
}
31+
32+
[Test]
33+
public void Build_WithSettingsFromEtcdAndCredentialsFromEnvironment_ValuesLoaded()
34+
{
35+
// Arrange
36+
37+
Environment.SetEnvironmentVariable("ETCD_TEST_USERNAME", "MyUserName");
38+
Environment.SetEnvironmentVariable("ETCD_TEST_PASSWORD", "passw");
39+
40+
var credentials = new Credentials("MyUserName", "passw");
41+
var envCredentials = Credentials.WithOverrideFromEnvironmentVariables("foo", "bar", "ETCD_TEST_USERNAME", "ETCD_TEST_PASSWORD");
2742

43+
var etcdSettings = new EtcdSettings("http://localhost:2379");
44+
45+
var config = new ConfigurationBuilder()
46+
.AddEtcd(credentials, etcdSettings)
47+
.AddEtcd(envCredentials, etcdSettings, "MyPrefix")
48+
.Build();
49+
50+
// Act
51+
PerformTest(config);
52+
53+
// Assert
54+
Assert.Pass("Credentials info: " + envCredentials.ToString());
55+
}
56+
57+
private static void PerformTest(IConfigurationRoot config)
58+
{
2859
var testSection = config.GetSection("TestSection");
2960
var testSubSection = testSection.GetSection("SubSection");
3061
var list = testSection.GetSection("ArraySection").Get<List<string>>();
3162
var testAppSection = config.GetSection("TestAppSection");
3263

3364
// Assert
3465

35-
Assert.IsNotNull(config);
36-
Assert.IsTrue(config.GetChildren().Any());
37-
Assert.IsTrue(testAppSection.GetChildren().Any());
38-
39-
Assert.AreEqual("Item 1 value", testSection["Item1"]);
40-
Assert.AreEqual("Item 2 value", testSection["Item2"]);
41-
Assert.AreEqual("Sub section value 1", testSubSection["Item1"]);
42-
Assert.AreEqual("Sub section value 2", testSubSection["Item2"]);
43-
Assert.AreEqual(2, list.Count);
44-
Assert.AreEqual("Item 1", list[0]);
45-
Assert.AreEqual("Item 2", list[1]);
46-
Assert.AreEqual("1234321", testAppSection["Item1"]);
66+
Assert.That(config, Is.Not.Null);
67+
Assert.That(config.GetChildren().Any());
68+
Assert.That(testAppSection.GetChildren().Any());
69+
70+
Assert.That(testSection["Item1"], Is.EqualTo("Item 1 value"));
71+
Assert.That(testSection["Item2"], Is.EqualTo("Item 2 value")); // Case insensitive key access
72+
Assert.That(testSubSection["Item1"], Is.EqualTo("Sub section value 1"));
73+
Assert.That(testSubSection["Item2"], Is.EqualTo("Sub section value 2"));
74+
Assert.That(list.Count, Is.EqualTo(2));
75+
Assert.That(list[0], Is.EqualTo("Item 1"));
76+
Assert.That(list[1], Is.EqualTo("Item 2"));
77+
Assert.That(testAppSection["Item1"], Is.EqualTo("1234321"));
4778
}
4879
}

0 commit comments

Comments
 (0)