Skip to content

Commit

Permalink
initial changes
Browse files Browse the repository at this point in the history
  • Loading branch information
kllysng committed Jun 13, 2024
1 parent d104c98 commit b02514c
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics.Contracts;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -22,6 +21,7 @@ public class ConfigurationManager<T> : BaseConfigurationManager, IConfigurationM
private DateTimeOffset _syncAfter = DateTimeOffset.MinValue;
private DateTimeOffset _lastRefresh = DateTimeOffset.MinValue;
private bool _isFirstRefreshRequest = true;
private bool _skipDistributedConfigurationManager = false;

private readonly SemaphoreSlim _refreshLock;
private readonly IDocumentRetriever _docRetriever;
Expand All @@ -30,6 +30,13 @@ public class ConfigurationManager<T> : BaseConfigurationManager, IConfigurationM
private T _currentConfiguration;
private Exception _fetchMetadataFailure;
private TimeSpan _bootstrapRefreshInterval = TimeSpan.FromSeconds(1);
private static readonly DistributedConfigurationOptions s_distributedConfigurationOptions = new();

// L2 TODO: internal until L2 cache is implemented S2S.
/// <summary>
/// TODO inject via ctors
/// </summary>
internal IDistributedConfigurationManager<T> DistributedConfigurationManager { get; set; }

/// <summary>
/// Instantiates a new <see cref="ConfigurationManager{T}"/> that manages automatic and controls refreshing on configuration data.
Expand Down Expand Up @@ -156,21 +163,54 @@ public async Task<T> GetConfigurationAsync(CancellationToken cancel)
{
try
{
// Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation.
// The transport should have it's own timeouts, etc..
var configuration = await _configRetriever.GetConfigurationAsync(MetadataAddress, _docRetriever, CancellationToken.None).ConfigureAwait(false);
if (_configValidator != null)
T configuration = null;
if (DistributedConfigurationManager != null && !_skipDistributedConfigurationManager)
{
ConfigurationValidationResult result = _configValidator.Validate(configuration);
if (!result.Succeeded)
throw LogHelper.LogExceptionMessage(new InvalidConfigurationException(LogHelper.FormatInvariant(LogMessages.IDX20810, result.ErrorMessage)));
// TODO handle try/catch
configuration = await DistributedConfigurationManager.GetConfigurationAsync(MetadataAddress, s_distributedConfigurationOptions, CancellationToken.None).ConfigureAwait(false);
if (_configValidator != null)
{
// TODO don't throw but log another exception
ConfigurationValidationResult result = _configValidator.Validate(configuration);
if (!result.Succeeded)
{
configuration = null;
LogHelper.LogExceptionMessage(new InvalidConfigurationException(LogHelper.FormatInvariant(LogMessages.IDX20810, result.ErrorMessage)));
}
}
}

_lastRefresh = DateTimeOffset.UtcNow;
// Add a random amount between 0 and 5% of AutomaticRefreshInterval jitter to avoid spike traffic to IdentityProvider.
_syncAfter = DateTimeUtil.Add(DateTime.UtcNow, AutomaticRefreshInterval +
TimeSpan.FromSeconds(new Random().Next((int)AutomaticRefreshInterval.TotalSeconds / 20)));
_currentConfiguration = configuration;
if (configuration == null)
{
// Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation.
// The transport should have it's own timeouts, etc..
configuration = await _configRetriever.GetConfigurationAsync(MetadataAddress, _docRetriever, CancellationToken.None).ConfigureAwait(false);
if (configuration is IConfigurationRetrievalTime configRetrievalTime)
configRetrievalTime.RetrievalTime = DateTimeOffset.UtcNow;

if (_configValidator != null)
{
ConfigurationValidationResult result = _configValidator.Validate(configuration);
if (!result.Succeeded)
throw LogHelper.LogExceptionMessage(new InvalidConfigurationException(LogHelper.FormatInvariant(LogMessages.IDX20810, result.ErrorMessage)));
}

if (_skipDistributedConfigurationManager)
_skipDistributedConfigurationManager = false;

if (DistributedConfigurationManager != null)
// TODO fire and forget (or not if refresh happens on a background thread)
await DistributedConfigurationManager.SetConfigurationAsync(MetadataAddress, configuration, s_distributedConfigurationOptions, CancellationToken.None).ConfigureAwait(false);

if (configuration is IConfigurationRetrievalTime configurationRetrievalTime)
_lastRefresh = configurationRetrievalTime.RetrievalTime;
else
_lastRefresh = DateTimeOffset.UtcNow;

// Add a random amount between 0-5% of AutomaticRefreshInterval jitter to avoid spiking traffic to IdP.
_syncAfter = DateTimeUtil.Add(_lastRefresh.DateTime, AutomaticRefreshInterval + TimeSpan.FromSeconds(new Random().Next((int)(AutomaticRefreshInterval.TotalSeconds / 20))));
_currentConfiguration = configuration;
}
}
catch (Exception ex)
{
Expand Down Expand Up @@ -250,6 +290,9 @@ public override void RequestRefresh()
{
_syncAfter = now;
_isFirstRefreshRequest = false;

if (DistributedConfigurationManager != null)
_skipDistributedConfigurationManager = true;
}
else if (now >= DateTimeUtil.Add(_lastRefresh.UtcDateTime, RefreshInterval))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

namespace Microsoft.IdentityModel.Protocols.Configuration
{
/// <summary>
///
/// </summary>
// L2 TODO: internal until L2 cache is implemented S2S.
internal class DistributedConfigurationOptions
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.IdentityModel.Protocols.Configuration
{
/// <summary>
///
/// </summary>
// L2 TODO: internal until L2 cache is implemented S2S.
internal interface IDistributedConfigurationManager<T> where T : class
{
/// <summary>
///
/// </summary>
/// <param name="metadataAddress"></param>
/// <param name="distributedConfigurationOptions"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<T> GetConfigurationAsync(string metadataAddress, DistributedConfigurationOptions distributedConfigurationOptions, CancellationToken cancellationToken = default);

/// <summary>
///
/// </summary>
/// <param name="metadataAddress"></param>
/// <param name="configuration"></param>
/// <param name="distributedConfigurationOptions"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task SetConfigurationAsync(string metadataAddress, T configuration, DistributedConfigurationOptions distributedConfigurationOptions, CancellationToken cancellationToken = default);
}
}
9 changes: 8 additions & 1 deletion src/Microsoft.IdentityModel.Tokens/BaseConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
Expand All @@ -10,7 +11,7 @@ namespace Microsoft.IdentityModel.Tokens
/// <summary>
/// Represents a generic metadata configuration which is applicable for both XML and JSON based configurations.
/// </summary>
public abstract class BaseConfiguration
public abstract class BaseConfiguration /*: IConfigurationRetrievalTime*/ // L2 TODO: internal until L2 cache is implemented S2S.
{
/// <summary>
/// Gets the issuer specified via the metadata endpoint.
Expand Down Expand Up @@ -46,5 +47,11 @@ public virtual ICollection<SecurityKey> TokenDecryptionKeys
{
get;
} = new Collection<SecurityKey>();

/// <summary>
///
/// </summary>
// L2 TODO: internal until L2 cache is implemented S2S.
internal DateTimeOffset RetrievalTime { get; set; }
}
}
17 changes: 17 additions & 0 deletions src/Microsoft.IdentityModel.Tokens/IConfigurationRetrievalTime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;

namespace Microsoft.IdentityModel.Tokens
{
/// <summary>
///
/// </summary>
// L2 TODO: internal until L2 cache is implemented S2S.
internal interface IConfigurationRetrievalTime
{
// L2 TODO: internal until L2 cache is implemented S2S.
internal DateTimeOffset RetrievalTime { get; set; }
}
}

0 comments on commit b02514c

Please sign in to comment.