Skip to content

Commit

Permalink
Adding Integration Test - Part 1 (#51)
Browse files Browse the repository at this point in the history
Adding Integration Test for Okta Setup Command
Reducing delay for Okta MFA verification
  • Loading branch information
vgmello authored Oct 23, 2024
1 parent 4788064 commit e003db4
Show file tree
Hide file tree
Showing 29 changed files with 623 additions and 101 deletions.
10 changes: 9 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,15 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_rule.non_public_constants_must_be_pascal_case.symbols = non_public_constants
dotnet_naming_rule.non_public_constants_must_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_public_constants_must_be_pascal_case.severity = suggestion

dotnet_naming_symbols.non_public_constants.applicable_kinds = field
dotnet_naming_symbols.non_public_constants.required_modifiers = const
dotnet_naming_symbols.non_public_constants.applicable_accessibilities = internal, private, protected, protected_internal

; lang style
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
Expand Down
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.*" developmentDependency="true">
<PackageReference Include="SonarAnalyzer.CSharp" developmentDependency="true">
<Version>9.*</Version>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@

<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.1.2"/>
<PackageReference Include="AWSSDK.RDS" Version="3.7.401.6" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.8" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"/>
<PackageReference Include="AWSSDK.RDS" Version="3.7.405" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.37" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0"/>
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0"/>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0"/>
Expand All @@ -56,7 +57,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Spectre.Console.Cli" Version="0.49.1"/>
<PackageReference Include="YamlDotNet" Version="16.0.0"/>
<PackageReference Include="YamlDotNet" Version="16.1.3" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,33 @@

namespace Ellosoft.AwsCredentialsManager.Infrastructure.Cli;

public class TypeRegistrar : ITypeRegistrar
public class TypeRegistrar(IServiceCollection services) : ITypeRegistrar
{
private readonly IServiceCollection _builder;
public ITypeResolver Build() => new TypeResolver(services.BuildServiceProvider());

public TypeRegistrar(IServiceCollection builder) => _builder = builder;
public void Register(Type service, Type implementation) => services.AddSingleton(service, new TypeWithPublicConstructors(implementation).Type);

public ITypeResolver Build() => new TypeResolver(_builder.BuildServiceProvider());

public void Register(Type service, Type implementation) => _builder.AddSingleton(service, new TypeWithPublicConstructors(implementation).Type);

public void RegisterInstance(Type service, object implementation) => _builder.AddSingleton(service, implementation);
public void RegisterInstance(Type service, object implementation) => services.AddSingleton(service, implementation);

public void RegisterLazy(Type service, Func<object> factory)
{
ArgumentNullException.ThrowIfNull(factory);
_builder.AddSingleton(service, _ => factory());
services.AddSingleton(service, _ => factory());
}

private sealed class TypeWithPublicConstructors
private sealed class TypeWithPublicConstructors(Type type)
{
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type Type { get; }

public TypeWithPublicConstructors(Type type) => Type = type;
public Type Type { get; } = type;
}

private sealed class TypeResolver : ITypeResolver, IDisposable
private sealed class TypeResolver(IServiceProvider provider) : ITypeResolver, IDisposable
{
private readonly IServiceProvider _provider;

public TypeResolver(IServiceProvider provider) => _provider = provider;

public object? Resolve(Type? type) => type is not null ? _provider.GetService(type) : null;
public object? Resolve(Type? type) => type is not null ? provider.GetService(type) : null;

public void Dispose()
{
if (_provider is IDisposable disposable)
if (provider is IDisposable disposable)
{
disposable.Dispose();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Ellosoft.AwsCredentialsManager/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

var services = new ServiceCollection()
.SetupLogging(logger)
.RegisterAppServices();
.AddAppServices();

var registrar = new TypeRegistrar(services);
var app = new CommandApp(registrar);
Expand Down Expand Up @@ -69,7 +69,7 @@
config.ValidateExamples();

if (System.Diagnostics.Debugger.IsAttached)
args = "rds pwd local".Split(' ');
args = "okta setup".Split(' ');
#endif
});

Expand Down
13 changes: 7 additions & 6 deletions src/Ellosoft.AwsCredentialsManager/ServiceRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Ellosoft.AwsCredentialsManager;

public static class ServiceRegistration
{
public static IServiceCollection RegisterAppServices(this IServiceCollection services)
public static IServiceCollection AddAppServices(this IServiceCollection services)
{
// core services
services
Expand All @@ -44,8 +44,9 @@ public static IServiceCollection RegisterAppServices(this IServiceCollection ser
.AddSingleton<IAwsCredentialsService, AwsCredentialsService>()
.AddSingleton<IAwsSamlService, AwsSamlService>();

services
.AddKeyedSingleton(nameof(OktaHttpClientFactory), OktaHttpClientFactory.CreateHttpClient());
services.AddHttpClient(nameof(OktaHttpClient), OktaHttpClient.Configure);
services.AddKeyedSingleton(nameof(OktaHttpClient), (provider, _)
=> provider.GetRequiredService<IHttpClientFactory>().CreateClient(nameof(OktaHttpClient)));

services
.AddSingleton<ICommandInterceptor, LogInterceptor>()
Expand Down Expand Up @@ -79,10 +80,10 @@ private static void RegisterWindowsServices(IServiceCollection services)
[SupportedOSPlatform("macos")]
private static void RegisterMacOSServices(IServiceCollection services)
{
services.AddSingleton<ISecureStorage, SecureStorageMacOS>();
services.TryAddSingleton<ISecureStorage, SecureStorageMacOS>();

// platform services
services.AddSingleton<IKeychainService, KeychainService>();
services.AddSingleton<IMacOsKeychainInterop, MacOsKeychainInterop>();
services.TryAddSingleton<IKeychainService, KeychainService>();
services.TryAddSingleton<IMacOsKeychainInterop, MacOsKeychainInterop>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Task<AuthenticationResult> Login(Uri oktaDomain, UserCredentials userCredentials
}

public class OktaLoginService(
IAnsiConsole console,
IConfigManager configManager,
IUserCredentialsManager userCredentialsManager,
IOktaClassicAuthenticator classicAuthenticator)
Expand Down Expand Up @@ -74,14 +75,14 @@ private void SaveUserCredentials(string userProfileKey, UserCredentials userCred
if (savedCredentials || !userCredentialsManager.SupportCredentialsStore)
return;

if (AnsiConsole.Confirm("Do you want to save your Okta username and password for future logins ?"))
if (console.Confirm("Do you want to save your Okta username and password for future logins ?"))
{
userCredentialsManager.SaveUserCredentials(userProfileKey, userCredentials);

return;
}

AnsiConsole.MarkupLine("[yellow]Ok... :([/]");
console.MarkupLine("[yellow]Ok... :([/]");
}

private UserCredentials GetUserCredentials(string userProfileKey, out bool savedCredentials)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public override async Task<FactorVerificationResponse> VerifyFactorAsync(Uri okt

while (factorResult == FactorResult.Waiting)
{
await Task.Delay(5_000);
await Task.Delay(2_000);

mfaAuthResponse = await VerifyFactorAsync(oktaDomain, factor.Id, verifyFactorRequest, Default.VerifyPushFactorRequest,
Default.FactorVerificationResponsePushOktaFactor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface IOktaClassicAuthenticator

public class OktaClassicAuthenticator(
ILogger<OktaClassicAuthenticator> logger,
[FromKeyedServices(nameof(OktaHttpClientFactory))] HttpClient httpClient,
[FromKeyedServices(nameof(OktaHttpClient))] HttpClient httpClient,
IOktaMfaFactorSelector mfaFactorSelector) : IOktaClassicAuthenticator
{
private readonly MfaHandlerProvider _mfaHandlerProvider = new();
Expand Down
13 changes: 13 additions & 0 deletions src/Ellosoft.AwsCredentialsManager/Services/Okta/OktaHttpClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2023 Ellosoft Limited. All rights reserved.

namespace Ellosoft.AwsCredentialsManager.Services.Okta;

public static class OktaHttpClient
{
public static void Configure(HttpClient httpClient)
{
const string USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0";

httpClient.DefaultRequestHeaders.Add("User-Agent", USER_AGENT);
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ public void Dispose()

protected virtual void Dispose(bool disposing)
{
if (!disposing) return;

Handle.SafeReleaseIntPrtMem();
}

protected static IntPtr GetClass(string name) => ObjectiveCRuntimeInterop.Instance.GetClass(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="Bogus" Version="35.6.1" />
<PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="NSubstitute" Version="5.1.0"/>
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Spectre.Console.Testing" Version="0.49.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Spectre.Console.Testing" Version="0.49.1"/>
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand All @@ -27,9 +29,13 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>


<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Ellosoft.AwsCredentialsManager\Ellosoft.AwsCredentialsManager.csproj"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2024 Ellosoft Limited. All rights reserved.

using Ellosoft.AwsCredentialsManager.Commands.Credentials;
using Ellosoft.AwsCredentialsManager.Infrastructure.Cli;
using Ellosoft.AwsCredentialsManager.Services.Configuration.Interactive;
using Microsoft.Extensions.DependencyInjection;

namespace Ellosoft.AwsCredentialsManager.Tests.Integration.Commands.Credentials;

public class CreateCredentialsProfileTests(ITestOutputHelper outputHelper, TestFixture testFixture) : IntegrationTest(outputHelper, testFixture)
{
[Fact(Skip = "WIP")]
public void CreateCredentialsProfile_Interactive_ShouldCreateNewProfile()
{
App.Configure(config =>
config.AddBranch<CredentialsBranch>(cred =>
cred.AddCommand<CreateCredentialsProfile>()));

var profileName = Guid.NewGuid().ToString("N");

var (exitCode, output) = App.Run("new", profileName);

exitCode.Should().Be(0);
output.Should().Contain($"'{profileName}' credentials created");

var credentialsManager = TestFixture.WebApp.Services.GetRequiredService<ICredentialsManager>();
credentialsManager.TryGetCredential(profileName, out var credentialsConfig);

credentialsConfig.Should().NotBeNull();
// credentialsConfig.RoleArn.Should().Be(awsRoleArn);
// credentialsConfig.OktaProfile.Should().Be(profileName);
// credentialsConfig.OktaAppUrl.Should().Be(oktaAppUrl);

}
}
Loading

0 comments on commit e003db4

Please sign in to comment.