Add ability to register and retrieve auth providers to/from IApplicationEnvironment (#360)
* Add ability to register and retrieve auth providers to/from IApplicationEnvironment * Fix docstring * Keep AppEnv as non-abstract
This commit is contained in:
Родитель
7da00b7bd9
Коммит
4f3c59d935
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.SourceParsing;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
|
@ -122,6 +123,14 @@ namespace Microsoft.Performance.SDK.Runtime
|
|||
args);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool TryGetAuthProvider<TAuth, TResult>(out IAuthProvider<TAuth, TResult> provider)
|
||||
where TAuth : IAuthMethod<TResult>
|
||||
{
|
||||
provider = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static MessageBoxIcon MapToImage(MessageType messageType)
|
||||
{
|
||||
switch (messageType)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.SourceParsing;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
|
@ -35,5 +36,12 @@ namespace Microsoft.Performance.SDK.Tests.TestClasses
|
|||
{
|
||||
return ButtonResult.None;
|
||||
}
|
||||
|
||||
public bool TryGetAuthProvider<TAuth, TResult>(out IAuthProvider<TAuth, TResult> provider)
|
||||
where TAuth : IAuthMethod<TResult>
|
||||
{
|
||||
provider = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Performance.SDK.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IAuthMethod{TResult}"/> that acquires bearer tokens for accessing Azure services.
|
||||
/// </summary>
|
||||
public sealed class AzureBearerTokenAuth
|
||||
: IAuthMethod<string>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AzureBearerTokenAuth"/> class.
|
||||
/// </summary>
|
||||
/// <param name="appId">
|
||||
/// The <see cref="Guid"/> of the Microsoft Entra ID (formerly Azure Active Directory) of the
|
||||
/// application to authenticate to.
|
||||
/// </param>
|
||||
/// <param name="scopes">
|
||||
/// The required scopes that the returned bearer token must have.
|
||||
/// </param>
|
||||
/// <param name="allowCache">
|
||||
/// A value indicating whether or not to allow the acquired token to come from a cache. This value may
|
||||
/// be <c>false</c> if a token for these scopes was previously acquired, but the token does not have access
|
||||
/// to the required resources. In this case, a different token (e.g. a token for a different user) may be
|
||||
/// required.
|
||||
/// </param>
|
||||
/// <param name="appName">
|
||||
/// A human-readable display name for the application the user is logging into.
|
||||
/// </param>
|
||||
/// <param name="authReason">
|
||||
/// An optional human-readable description to describe why the authentication is needed. For example,
|
||||
/// "retrieve data from Azure DevOps".
|
||||
/// </param>
|
||||
/// <param name="tenant">
|
||||
/// The optional tenant the authenticated principal must be logged into. May be <c>null</c>.
|
||||
/// </param>
|
||||
/// <param name="preferredAccountDomain">
|
||||
/// The optional domain name of accounts which should be preferred for this authentication request.
|
||||
/// May be <c>null</c>.
|
||||
/// </param>
|
||||
public AzureBearerTokenAuth(
|
||||
Guid appId,
|
||||
string[] scopes,
|
||||
bool allowCache,
|
||||
string appName,
|
||||
string authReason,
|
||||
Guid? tenant,
|
||||
string preferredAccountDomain)
|
||||
{
|
||||
AppId = appId;
|
||||
Scopes = scopes;
|
||||
AllowCache = allowCache;
|
||||
AppName = appName;
|
||||
AuthReason = authReason;
|
||||
Tenant = tenant;
|
||||
PreferredAccountDomain = preferredAccountDomain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Guid"/> of the Microsoft Entra ID (formerly Azure Active Directory) of the
|
||||
/// application to authenticate to.
|
||||
/// </summary>
|
||||
public Guid AppId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the required scopes that the returned bearer token must have.
|
||||
/// </summary>
|
||||
public string[] Scopes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether or not to allow the acquired token to come from a cache. This value may
|
||||
/// be <c>false</c> if a token for these scopes was previously acquired, but the token does not have access
|
||||
/// to the required resources. In this case, a different token (e.g. a token for a different user) may be
|
||||
/// required.
|
||||
/// </summary>
|
||||
public bool AllowCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human-readable display name for the application the user is logging into.
|
||||
/// </summary>
|
||||
public string AppName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an optional human-readable description to describe why the authentication is needed. For example,
|
||||
/// "retrieve data from Azure DevOps".
|
||||
/// </summary>
|
||||
public string AuthReason { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional tenant the authenticated principal must be logged into. May be <c>null</c>.
|
||||
/// </summary>
|
||||
public Guid? Tenant { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optional domain name of accounts which should be preferred for this authentication request.
|
||||
/// May be <c>null</c>.
|
||||
/// </summary>
|
||||
public string PreferredAccountDomain { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace Microsoft.Performance.SDK.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an authentication method that can be used to authenticate to a service.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">
|
||||
/// The type of the result of a successful authentication.
|
||||
/// </typeparam>
|
||||
public interface IAuthMethod<TResult>
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Performance.SDK.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides authentication for a given <see cref="IAuthMethod{TResult}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAuth">
|
||||
/// The type of the <see cref="IAuthMethod{TResult}"/> this provider can authenticate.
|
||||
/// </typeparam>
|
||||
/// <typeparam name="TResult">
|
||||
/// The type of the result of a successful authentication.
|
||||
/// </typeparam>
|
||||
public interface IAuthProvider<in TAuth, TResult>
|
||||
where TAuth : IAuthMethod<TResult>
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to authenticate the given <see cref="IAuthMethod{TResult}"/>.
|
||||
/// </summary>
|
||||
/// <param name="authRequest">
|
||||
/// The <see cref="IAuthMethod{TResult}"/> to authenticate.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// An awaitable task that, upon completion, will contain the result of the authentication request.
|
||||
/// The result of the authentication will either be an instance of <typeparamref name="TResult"/> if
|
||||
/// the authentication was successful, or <c>null</c> if the authentication failed.
|
||||
/// </returns>
|
||||
Task<TResult> TryGetAuth(TAuth authRequest);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.SourceParsing;
|
||||
|
||||
|
@ -117,5 +118,26 @@ namespace Microsoft.Performance.SDK.Processing
|
|||
string caption,
|
||||
string format,
|
||||
params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to get an <see cref="IAuthProvider{TAuth, TResult}"/> that can provide authentication
|
||||
/// for <see cref="IAuthMethod{TResult}"/> of type <typeparamref name="TAuth"/>.
|
||||
/// </summary>
|
||||
/// <param name="provider">
|
||||
/// The found provider, or <c>null</c> if no registered provider can provide authentication for
|
||||
/// <typeparamref name="TAuth"/>.
|
||||
/// </param>
|
||||
/// <typeparam name="TAuth">
|
||||
/// The type of the <see cref="IAuthMethod{TResult}"/> for which to attempt to get a provider.
|
||||
/// </typeparam>
|
||||
/// <typeparam name="TResult">
|
||||
/// The type of the result of a successful authentication for <typeparamref name="TAuth"/>.
|
||||
/// </typeparam>
|
||||
/// <returns>
|
||||
/// <c>true</c> if a provider was found; <c>false</c> otherwise. If <c>false</c> is returned,
|
||||
/// <paramref name="provider"/> will be <c>null</c>.
|
||||
/// </returns>
|
||||
bool TryGetAuthProvider<TAuth, TResult>(out IAuthProvider<TAuth, TResult> provider)
|
||||
where TAuth : IAuthMethod<TResult>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.SourceParsing;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
|
@ -35,5 +36,12 @@ namespace Microsoft.Performance.Testing.SDK
|
|||
{
|
||||
return ButtonResult.None;
|
||||
}
|
||||
|
||||
public bool TryGetAuthProvider<TAuth, TResult>(out IAuthProvider<TAuth, TResult> provider)
|
||||
where TAuth : IAuthMethod<TResult>
|
||||
{
|
||||
provider = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ namespace Microsoft.Performance.Toolkit.Engine.Tests.TestCookers.Source123
|
|||
|
||||
public bool IsDisposed => this.disposedValue;
|
||||
|
||||
internal IApplicationEnvironment ApplicationEnvironmentSpy => this.ApplicationEnvironment;
|
||||
|
||||
protected override ICustomDataProcessor CreateProcessorCore(
|
||||
IEnumerable<IDataSource> dataSources,
|
||||
IProcessorEnvironment processorEnvironment,
|
||||
|
|
|
@ -7,6 +7,8 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Extensibility;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using Microsoft.Performance.SDK.Runtime;
|
||||
|
@ -1131,6 +1133,55 @@ namespace Microsoft.Performance.Toolkit.Engine.Tests
|
|||
|
||||
#endregion
|
||||
|
||||
#region Auth
|
||||
|
||||
[TestMethod]
|
||||
[UnitTest]
|
||||
public void ProcessingSources_AreGiven_SpecifiedAuthProviders()
|
||||
{
|
||||
var givenProvider = new StubAuthProvider();
|
||||
|
||||
var info = new EngineCreateInfo(this.DefaultSet.AsReadOnly());
|
||||
info.WithAuthProvider(givenProvider);
|
||||
using var sut = Engine.Create(info);
|
||||
|
||||
var spy = sut.Plugins.ProcessingSourceReferences.First(psr => psr.Guid == Source123DataSource.Guid).Instance as Source123DataSource;
|
||||
var success = spy.ApplicationEnvironmentSpy.TryGetAuthProvider<StubAuthMethod, int>(out var foundProvider);
|
||||
|
||||
Assert.IsTrue(success);
|
||||
Assert.AreEqual(givenProvider, foundProvider);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[UnitTest]
|
||||
public void TryGetAuthProvider_Fails_WhenNoProviderIsRegistered()
|
||||
{
|
||||
var info = new EngineCreateInfo(this.DefaultSet.AsReadOnly());
|
||||
using var sut = Engine.Create(info);
|
||||
|
||||
var spy = sut.Plugins.ProcessingSourceReferences.First(psr => psr.Guid == Source123DataSource.Guid).Instance as Source123DataSource;
|
||||
var success = spy.ApplicationEnvironmentSpy.TryGetAuthProvider<StubAuthMethod, int>(out var foundProvider);
|
||||
|
||||
Assert.IsFalse(success);
|
||||
Assert.IsNull(foundProvider);
|
||||
}
|
||||
|
||||
private class StubAuthMethod
|
||||
: IAuthMethod<int>
|
||||
{
|
||||
}
|
||||
|
||||
private class StubAuthProvider
|
||||
: IAuthProvider<StubAuthMethod, int>
|
||||
{
|
||||
public Task<int> TryGetAuth(StubAuthMethod authRequest)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static DataOutputPath Parse(string dataCookerOutputPath)
|
||||
{
|
||||
var split = dataCookerOutputPath.Split('/');
|
||||
|
|
|
@ -770,7 +770,7 @@ namespace Microsoft.Performance.Toolkit.Engine
|
|||
? createInfo.ApplicationName
|
||||
: string.Empty;
|
||||
|
||||
instance.applicationEnvironment = new ApplicationEnvironment(
|
||||
instance.applicationEnvironment = new EngineApplicationEnvironment(
|
||||
applicationName: applicationName,
|
||||
runtimeName: runtimeName,
|
||||
new RuntimeTableSynchronizer(),
|
||||
|
@ -778,7 +778,8 @@ namespace Microsoft.Performance.Toolkit.Engine
|
|||
instance.Factory.CreateSourceSessionFactory(),
|
||||
createInfo.IsInteractive
|
||||
? (IMessageBox)new InteractiveRuntimeMessageBox(instance.logger)
|
||||
: (IMessageBox)new NonInteractiveMessageBox(instance.logger))
|
||||
: (IMessageBox)new NonInteractiveMessageBox(instance.logger),
|
||||
createInfo.AuthProviders)
|
||||
{
|
||||
IsInteractive = createInfo.IsInteractive,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Extensibility.DataCooking;
|
||||
using Microsoft.Performance.SDK.Extensibility.SourceParsing;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
using Microsoft.Performance.SDK.Runtime;
|
||||
|
||||
namespace Microsoft.Performance.Toolkit.Engine
|
||||
{
|
||||
internal sealed class EngineApplicationEnvironment
|
||||
: ApplicationEnvironment
|
||||
{
|
||||
private readonly ReadOnlyDictionary<Type, object> authProviders;
|
||||
|
||||
public EngineApplicationEnvironment(
|
||||
string applicationName,
|
||||
string runtimeName,
|
||||
ITableDataSynchronization tableDataSynchronizer,
|
||||
ISourceDataCookerFactoryRetrieval sourceDataCookerFactory,
|
||||
ISourceSessionFactory sourceSessionFactory,
|
||||
IMessageBox messageBox,
|
||||
ReadOnlyDictionary<Type, object> authProviders)
|
||||
: base(
|
||||
applicationName,
|
||||
runtimeName,
|
||||
tableDataSynchronizer,
|
||||
sourceDataCookerFactory,
|
||||
sourceSessionFactory,
|
||||
messageBox)
|
||||
{
|
||||
this.authProviders = authProviders;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool TryGetAuthProvider<TAuth, TResult>(out IAuthProvider<TAuth, TResult> provider)
|
||||
{
|
||||
if (this.authProviders.TryGetValue(typeof(TAuth), out var authProvider))
|
||||
{
|
||||
var authProviderTyped = authProvider as IAuthProvider<TAuth, TResult>;
|
||||
|
||||
if (authProviderTyped == null)
|
||||
{
|
||||
Debug.Fail($"Expected registered {nameof(authProvider)} for type {typeof(TAuth)} to be of type {nameof(IAuthProvider<TAuth, TResult>)}");
|
||||
|
||||
provider = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
provider = authProviderTyped;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.TryGetAuthProvider(out provider);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.Performance.SDK;
|
||||
using Microsoft.Performance.SDK.Auth;
|
||||
using Microsoft.Performance.SDK.Processing;
|
||||
|
||||
namespace Microsoft.Performance.Toolkit.Engine
|
||||
|
@ -16,6 +18,8 @@ namespace Microsoft.Performance.Toolkit.Engine
|
|||
{
|
||||
private static string DefaultRuntimeName;
|
||||
|
||||
private readonly Dictionary<Type, object> authProviders = new Dictionary<Type, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the statc members of the <see cref="EngineCreateInfo"/>
|
||||
/// class.
|
||||
|
@ -97,6 +101,39 @@ namespace Microsoft.Performance.Toolkit.Engine
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an <see cref="IAuthProvider{TAuth, TResult}"/> of type <typeparamref name="TAuth"/> to be available to plugins
|
||||
/// via <see cref="IApplicationEnvironment.TryGetAuthProvider{TAuth, TResult}"/>.
|
||||
/// </summary>
|
||||
/// <param name="provider">
|
||||
/// The <see cref="IAuthProvider{TAuth, TResult}"/> to register.
|
||||
/// </param>
|
||||
/// <typeparam name="TAuth">
|
||||
/// The type of <see cref="IAuthMethod{TResult}"/> for which the <see cref="IAuthProvider{TAuth, TResult}"/> is being registered.
|
||||
/// </typeparam>
|
||||
/// <typeparam name="TResult">
|
||||
/// The type of the result of successful authentication requests for <typeparamref name="TAuth"/>.
|
||||
/// </typeparam>
|
||||
/// <returns>
|
||||
/// The instance of <see cref="EngineCreateInfo"/>.
|
||||
/// </returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// An <see cref="IAuthProvider{TAuth, TResult}"/> for the specified type <typeparamref name="TAuth"/> already exists.
|
||||
/// </exception>
|
||||
public EngineCreateInfo WithAuthProvider<TAuth, TResult>(IAuthProvider<TAuth, TResult> provider)
|
||||
where TAuth : IAuthMethod<TResult>
|
||||
{
|
||||
Guard.NotNull(provider, nameof(provider));
|
||||
|
||||
var toRegister = typeof(TAuth);
|
||||
if (!this.authProviders.TryAdd(toRegister, provider))
|
||||
{
|
||||
throw new InvalidOperationException($"An auth provider for the specified type {toRegister} already exists.");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the runtime on which the application is built.
|
||||
/// </summary>
|
||||
|
@ -136,5 +173,7 @@ namespace Microsoft.Performance.Toolkit.Engine
|
|||
/// will fail. By default, this property is <c>false</c>.
|
||||
/// </summary>
|
||||
public bool IsInteractive { get; set; }
|
||||
|
||||
internal ReadOnlyDictionary<Type, object> AuthProviders => new ReadOnlyDictionary<Type, object>(this.authProviders);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче