Skip to content

Commit 19bd5d0

Browse files
Finally CachedCredentialsProvider.cs
1 parent 34eee10 commit 19bd5d0

File tree

3 files changed

+192
-6
lines changed

3 files changed

+192
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
- **Breaking Change**: `Ydb.Sdk.Yc.Auth` version <= 0.1.0 is not compatible with newer versions.
22
- Added `IAuthClient` to fetch auth token.
3-
- Added the `TokenManagerCredentialsProvider` class, which streamlines token lifecycle management.
3+
- Added the `CachedCredentialsProvider` class, which streamlines token lifecycle management.
44
- **Breaking Change**: Deleted `AnonymousProvider`. Now users don't need to do anything for anonymous authentication.
55
Migration guide:
66
```c#

src/Ydb.Sdk/src/Auth/CachedCredentialsProvider.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,22 @@ void Init()
5757
}
5858
}
5959

60+
private class InitState : ITokenState
61+
{
62+
private readonly SyncState _syncState;
63+
64+
public InitState(SyncState syncState)
65+
{
66+
syncState.Init();
67+
_syncState = syncState;
68+
}
69+
70+
public TokenResponse TokenResponse => _syncState.TokenResponse;
71+
72+
public async ValueTask<ITokenState> Validate(DateTime now) =>
73+
await (await _syncState.Validate(now)).Validate(now);
74+
}
75+
6076
private class ActiveState : ITokenState
6177
{
6278
private readonly CachedCredentialsProvider _cachedCredentialsProvider;
@@ -114,8 +130,8 @@ public async ValueTask<ITokenState> Validate(DateTime now)
114130
var tokenResponse = await _fetchTokenTask;
115131

116132
_cachedCredentialsProvider.Logger.LogDebug(
117-
"Successfully fetched token at {Timestamp}. ExpiredAt: {ExpiredAt}, RefreshAt: {RefreshAt}",
118-
DateTime.Now, tokenResponse.ExpiredAt, tokenResponse.RefreshAt
133+
"Successfully fetched token. ExpiredAt: {ExpiredAt}, RefreshAt: {RefreshAt}",
134+
tokenResponse.ExpiredAt, tokenResponse.RefreshAt
119135
);
120136

121137
return _cachedCredentialsProvider.UpdateState(this,
@@ -125,8 +141,7 @@ public async ValueTask<ITokenState> Validate(DateTime now)
125141
{
126142
_cachedCredentialsProvider.Logger.LogCritical(e, "Error on authentication token update");
127143

128-
return _cachedCredentialsProvider.UpdateState(this,
129-
new ErrorState(e, _cachedCredentialsProvider));
144+
return _cachedCredentialsProvider.UpdateState(this, new ErrorState(e, _cachedCredentialsProvider));
130145
}
131146
}
132147

@@ -158,8 +173,9 @@ public async ValueTask<ITokenState> Validate(DateTime now)
158173
);
159174

160175
return now >= TokenResponse.ExpiredAt
161-
? _cachedCredentialsProvider
176+
? await _cachedCredentialsProvider
162177
.UpdateState(this, new SyncState(_cachedCredentialsProvider))
178+
.Validate(now)
163179
: _cachedCredentialsProvider
164180
.UpdateState(this, new BackgroundState(TokenResponse, _cachedCredentialsProvider));
165181
}

src/Ydb.Sdk/tests/Auth/CachedCredentialsProviderTests.cs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,174 @@ public async Task SyncState_To_ActiveState_To_SyncState_To_ActiveState()
8585
Assert.Equal(Token + "1", await credentialsProvider.GetAuthInfoAsync());
8686
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(2));
8787
}
88+
89+
[Fact]
90+
public async Task
91+
SyncState_To_ActiveState_To_BackgroundState_To_ErrorState_When_Task_Is_Canceled_To_SyncState_To_ActiveState()
92+
{
93+
var now = DateTime.UtcNow;
94+
var tcsTokenResponse = new TaskCompletionSource<TokenResponse>();
95+
96+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
97+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(3))))
98+
.Returns(tcsTokenResponse.Task)
99+
.ReturnsAsync(new TokenResponse(Token + Token, now.Add(TimeSpan.FromSeconds(10))));
100+
_mockClock.SetupSequence(clock => clock.UtcNow)
101+
.Returns(now)
102+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
103+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
104+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
105+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
106+
var credentialsProvider = new CachedCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
107+
108+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
109+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
110+
var taskOnError = credentialsProvider.GetAuthInfoAsync();
111+
tcsTokenResponse.SetCanceled();
112+
await Assert.ThrowsAsync<TaskCanceledException>(async () => await taskOnError);
113+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
114+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
115+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
116+
}
117+
118+
[Fact]
119+
public async Task
120+
SyncState_To_ActiveState_To_BackgroundState_To_ErrorState_When_Task_Is_Faulted_To_SyncState_To_ActiveState()
121+
{
122+
var now = DateTime.UtcNow;
123+
var tcsTokenResponse = new TaskCompletionSource<TokenResponse>();
124+
125+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
126+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(3))))
127+
.Returns(tcsTokenResponse.Task)
128+
.ReturnsAsync(new TokenResponse(Token + Token, now.Add(TimeSpan.FromSeconds(10))));
129+
_mockClock.SetupSequence(clock => clock.UtcNow)
130+
.Returns(now)
131+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
132+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
133+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
134+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
135+
var credentialsProvider = new CachedCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
136+
137+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
138+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
139+
var taskOnError = credentialsProvider.GetAuthInfoAsync();
140+
tcsTokenResponse.SetException(new StatusUnsuccessfulException(new Status(StatusCode.Unavailable)));
141+
await Assert.ThrowsAsync<StatusUnsuccessfulException>(async () => await taskOnError);
142+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
143+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
144+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
145+
}
146+
147+
[Fact]
148+
public async Task
149+
SyncState_To_ActiveState_To_BackgroundState_To_SyncState_When_Task_Is_Canceled_To_ActiveState()
150+
{
151+
var now = DateTime.UtcNow;
152+
var tcsTokenResponse = new TaskCompletionSource<TokenResponse>();
153+
154+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
155+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(3))))
156+
.Returns(tcsTokenResponse.Task)
157+
.ReturnsAsync(new TokenResponse(Token + Token, now.Add(TimeSpan.FromSeconds(10))));
158+
_mockClock.SetupSequence(clock => clock.UtcNow)
159+
.Returns(now)
160+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
161+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
162+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
163+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
164+
var credentialsProvider = new CachedCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
165+
166+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
167+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
168+
tcsTokenResponse.SetCanceled();
169+
var taskOnBackground = credentialsProvider.GetAuthInfoAsync();
170+
Assert.Equal(Token + Token, await taskOnBackground);
171+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
172+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
173+
}
174+
175+
[Fact]
176+
public async Task
177+
SyncState_To_ActiveState_To_BackgroundState_To_SyncState_When_Task_Is_Fauleted_To_ActiveState()
178+
{
179+
var now = DateTime.UtcNow;
180+
var tcsTokenResponse = new TaskCompletionSource<TokenResponse>();
181+
182+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
183+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(3))))
184+
.Returns(tcsTokenResponse.Task)
185+
.ReturnsAsync(new TokenResponse(Token + Token, now.Add(TimeSpan.FromSeconds(10))));
186+
_mockClock.SetupSequence(clock => clock.UtcNow)
187+
.Returns(now)
188+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
189+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
190+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
191+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
192+
var credentialsProvider = new CachedCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
193+
194+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
195+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
196+
tcsTokenResponse.SetException(new StatusUnsuccessfulException(new Status(StatusCode.Unavailable)));
197+
var taskOnBackground = credentialsProvider.GetAuthInfoAsync();
198+
Assert.Equal(Token + Token, await taskOnBackground);
199+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
200+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
201+
}
202+
203+
[Fact]
204+
public async Task
205+
SyncState_To_ActiveState_To_BackgroundState_To_BackgroundState_When_Task_Is_Canceled_To_ActiveState()
206+
{
207+
var now = DateTime.UtcNow;
208+
var tcsTokenResponse = new TaskCompletionSource<TokenResponse>();
209+
210+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
211+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(3))))
212+
.Returns(tcsTokenResponse.Task)
213+
.ReturnsAsync(new TokenResponse(Token + Token, now.Add(TimeSpan.FromSeconds(10))));
214+
_mockClock.SetupSequence(clock => clock.UtcNow)
215+
.Returns(now)
216+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
217+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
218+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
219+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
220+
var credentialsProvider = new CachedCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
221+
222+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
223+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
224+
tcsTokenResponse.SetCanceled();
225+
var taskOnBackground = credentialsProvider.GetAuthInfoAsync();
226+
Assert.Equal(Token, await taskOnBackground);
227+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
228+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
229+
}
230+
231+
[Fact]
232+
public async Task
233+
SyncState_To_ActiveState_To_BackgroundState_To_BackgroundState_When_Task_Is_Fauleted_To_ActiveState()
234+
{
235+
var now = DateTime.UtcNow;
236+
var tcsTokenResponse = new TaskCompletionSource<TokenResponse>();
237+
238+
_mockAuthClient.SetupSequence(authClient => authClient.FetchToken())
239+
.ReturnsAsync(new TokenResponse(Token, now.Add(TimeSpan.FromSeconds(3))))
240+
.Returns(tcsTokenResponse.Task)
241+
.ReturnsAsync(new TokenResponse(Token + Token, now.Add(TimeSpan.FromSeconds(10))));
242+
_mockClock.SetupSequence(clock => clock.UtcNow)
243+
.Returns(now)
244+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
245+
.Returns(now.Add(TimeSpan.FromSeconds(2)))
246+
.Returns(now.Add(TimeSpan.FromSeconds(4)))
247+
.Returns(now.Add(TimeSpan.FromSeconds(4)));
248+
var credentialsProvider = new CachedCredentialsProvider(_mockAuthClient.Object, _mockClock.Object);
249+
250+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
251+
Assert.Equal(Token, await credentialsProvider.GetAuthInfoAsync());
252+
tcsTokenResponse.SetException(new StatusUnsuccessfulException(new Status(StatusCode.Unavailable)));
253+
var taskOnBackground = credentialsProvider.GetAuthInfoAsync();
254+
Assert.Equal(Token, await taskOnBackground);
255+
Assert.Equal(Token + Token, await credentialsProvider.GetAuthInfoAsync());
256+
_mockAuthClient.Verify(authClient => authClient.FetchToken(), Times.Exactly(3));
257+
}
88258
}

0 commit comments

Comments
 (0)