From 07b383da5b14fdd024899a0aa3c4f9f8ffbd1905 Mon Sep 17 00:00:00 2001 From: Vitor M <4777793+vgmello@users.noreply.github.com> Date: Fri, 8 Dec 2023 01:02:12 +0000 Subject: [PATCH] Fixes multi-session bug preventing users from having simultaneous AWS sessions active (#23) Fixes Multi Session bug preventing users from having simultaneous AWS sessions active --- Directory.Build.props | 2 +- .../Credentials/CreateCredentialsProfile.cs | 9 ++++--- .../Commands/Credentials/GetCredentials.cs | 21 +++++---------- .../Credentials/ListCredentialsProfiles.cs | 12 +++------ .../Commands/RDS/GetRdsPassword.cs | 2 +- .../Ellosoft.AwsCredentialsManager.csproj | 4 +-- .../AWS/Interactive/AwsOktaSessionManager.cs | 27 ++++++++++++------- .../Interactive/CredentialsManager.cs | 4 +-- .../Interactive/EnvironmentManager.cs | 2 +- .../Models/CredentialsConfiguration.cs | 7 ++++- 10 files changed, 46 insertions(+), 44 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d862dbc..7acb831 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/CreateCredentialsProfile.cs b/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/CreateCredentialsProfile.cs index ba5bcce..18e1519 100644 --- a/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/CreateCredentialsProfile.cs +++ b/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/CreateCredentialsProfile.cs @@ -17,6 +17,8 @@ namespace Ellosoft.AwsCredentialsManager.Commands.Credentials; [Examples("new prod")] public class CreateCredentialsProfile : AsyncCommand { + private const string DEFAULT_AWS_PROFILE_VALUE = "[credential name]"; + public class Settings : AwsSettings { [CommandArgument(0, "")] @@ -27,9 +29,9 @@ public class Settings : AwsSettings [Description("AWS role ARN")] public string? AwsRoleArn { get; set; } - [CommandOption("-p|--aws-profile")] + [CommandOption("--aws-profile")] [Description("AWS profile to use (profile used in AWS CLI)")] - [DefaultValue("default")] + [DefaultValue(DEFAULT_AWS_PROFILE_VALUE)] public string? AwsProfile { get; set; } [CommandOption("--okta-app-url")] @@ -63,10 +65,11 @@ public override async Task ExecuteAsync(CommandContext context, Settings se { var oktaAppUrl = settings.OktaAppUrl ?? await GetAwsAppUrl(settings.OktaUserProfile); var awsRole = settings.AwsRoleArn ?? await GetAwsRoleArn(settings.OktaUserProfile, oktaAppUrl); + var awsProfile = settings.AwsProfile is null or DEFAULT_AWS_PROFILE_VALUE ? null : settings.AwsProfile; _credentialsManager.CreateCredential( name: settings.Name, - awsProfile: settings.AwsProfile!, + awsProfile: awsProfile, awsRole: awsRole, oktaAppUrl: oktaAppUrl, oktaProfile: settings.OktaUserProfile); diff --git a/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/GetCredentials.cs b/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/GetCredentials.cs index d428b6e..b76d8dd 100644 --- a/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/GetCredentials.cs +++ b/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/GetCredentials.cs @@ -11,7 +11,10 @@ namespace Ellosoft.AwsCredentialsManager.Commands.Credentials; [Examples( "get prod", "get prod --aws-profile default")] -public class GetCredentials : AsyncCommand +public class GetCredentials( + CredentialsManager credentialsManager, + AwsOktaSessionManager sessionManager +) : AsyncCommand { public class Settings : AwsSettings { @@ -25,23 +28,11 @@ public class Settings : AwsSettings public string? AwsProfile { get; set; } } - private readonly CredentialsManager _credentialsManager; - private readonly AwsOktaSessionManager _sessionManager; - - public GetCredentials( - CredentialsManager credentialsManager, - AwsOktaSessionManager sessionManager - ) - { - _credentialsManager = credentialsManager; - _sessionManager = sessionManager; - } - public override async Task ExecuteAsync(CommandContext context, Settings settings) { - var credential = settings.Credential ?? _credentialsManager.GetCredential(); + var credential = settings.Credential ?? credentialsManager.GetCredentialNameFromUser(); - var awsCredentials = await _sessionManager.CreateOrResumeSessionAsync(credential, settings.AwsProfile); + var awsCredentials = await sessionManager.CreateOrResumeSessionAsync(credential, settings.AwsProfile); if (awsCredentials is null) return 1; diff --git a/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/ListCredentialsProfiles.cs b/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/ListCredentialsProfiles.cs index cc2cc76..637da03 100644 --- a/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/ListCredentialsProfiles.cs +++ b/src/Ellosoft.AwsCredentialsManager/Commands/Credentials/ListCredentialsProfiles.cs @@ -10,7 +10,7 @@ namespace Ellosoft.AwsCredentialsManager.Commands.Credentials; [Name("list"), Alias("ls")] [Description("List saved credential profiles")] [Examples("ls")] -public class ListCredentialsProfiles : Command +public class ListCredentialsProfiles(IConfigManager configManager) : Command { public class Settings : AwsSettings { @@ -20,13 +20,9 @@ public class Settings : AwsSettings public string OktaUserProfile { get; set; } = OktaConfiguration.DefaultProfileName; } - private readonly IConfigManager _configManager; - - public ListCredentialsProfiles(IConfigManager configManager) => _configManager = configManager; - public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings) { - var credentials = _configManager.AppConfig.Credentials; + var credentials = configManager.AppConfig.Credentials; var table = new Table() .Title("[green]Saved credentials[/]") @@ -36,8 +32,8 @@ public override int Execute([NotNull] CommandContext context, [NotNull] Settings var filteredCredentials = credentials.Where(kv => kv.Value.OktaProfile == settings.OktaUserProfile); - foreach (var credential in filteredCredentials) - table.AddRow(credential.Key, credential.Value.RoleArn, credential.Value.AwsProfile); + foreach (var (credentialName, credentialConfig) in filteredCredentials) + table.AddRow(credentialName, credentialConfig.RoleArn, credentialConfig.GetAwsProfileSafe(credentialName)); AnsiConsole.Write(table); diff --git a/src/Ellosoft.AwsCredentialsManager/Commands/RDS/GetRdsPassword.cs b/src/Ellosoft.AwsCredentialsManager/Commands/RDS/GetRdsPassword.cs index 8c05a19..50510fe 100644 --- a/src/Ellosoft.AwsCredentialsManager/Commands/RDS/GetRdsPassword.cs +++ b/src/Ellosoft.AwsCredentialsManager/Commands/RDS/GetRdsPassword.cs @@ -88,7 +88,7 @@ await GenerateDbPassword( private async Task HandleAdHocRequest(Settings settings) { - var credentialName = _credentialsManager.GetCredential(); + var credentialName = _credentialsManager.GetCredentialNameFromUser(); AnsiConsole.MarkupLine($"Getting RDS password using [green i]{credentialName}[/] credential profile"); diff --git a/src/Ellosoft.AwsCredentialsManager/Ellosoft.AwsCredentialsManager.csproj b/src/Ellosoft.AwsCredentialsManager/Ellosoft.AwsCredentialsManager.csproj index 8d2991b..46d7345 100644 --- a/src/Ellosoft.AwsCredentialsManager/Ellosoft.AwsCredentialsManager.csproj +++ b/src/Ellosoft.AwsCredentialsManager/Ellosoft.AwsCredentialsManager.csproj @@ -44,8 +44,8 @@ - - + + diff --git a/src/Ellosoft.AwsCredentialsManager/Services/AWS/Interactive/AwsOktaSessionManager.cs b/src/Ellosoft.AwsCredentialsManager/Services/AWS/Interactive/AwsOktaSessionManager.cs index 7d495ce..3b8bf12 100644 --- a/src/Ellosoft.AwsCredentialsManager/Services/AWS/Interactive/AwsOktaSessionManager.cs +++ b/src/Ellosoft.AwsCredentialsManager/Services/AWS/Interactive/AwsOktaSessionManager.cs @@ -17,19 +17,19 @@ public class AwsOktaSessionManager( private readonly AwsCredentialsService _awsCredentialsService = new(); private readonly AwsSamlService _awsSamlService = new(); - public async Task CreateOrResumeSessionAsync(string credentialProfile, string? awsProfile) + public async Task CreateOrResumeSessionAsync(string credentialProfile, string? outputAwsProfile) { if (!credentialsManager.TryGetCredential(credentialProfile, out var credentialsConfig)) return null; - var awsProfileName = awsProfile ?? credentialsConfig.AwsProfile; + var awsProfile = credentialsConfig.GetAwsProfileSafe(credentialProfile); - if (TryResumeSession(awsProfileName, credentialsConfig.RoleArn, out var awsCredentialsData)) - return CreateAwsCredentials(awsCredentialsData); + if (TryResumeSession(awsProfile, credentialsConfig.RoleArn, out var awsCredentialsData)) + return CreateAwsCredentials(awsCredentialsData, awsProfile, outputAwsProfile); - var newCredential = await CreateSessionAsync(credentialProfile, awsProfileName, credentialsConfig); + var newCredential = await CreateSessionAsync(credentialProfile, awsProfile, credentialsConfig); - return newCredential is not null ? CreateAwsCredentials(newCredential) : null; + return newCredential is not null ? CreateAwsCredentials(newCredential, awsProfile, outputAwsProfile) : null; } private bool TryResumeSession(string awsProfile, string roleArn, [NotNullWhen(true)] out AwsCredentialsData? credentialsData) @@ -58,7 +58,7 @@ Do you want renew the credentials now ?[/] return true; } - private async Task CreateSessionAsync(string credentialProfile, string awsProfileName, CredentialsConfiguration credentialsConfig) + private async Task CreateSessionAsync(string credentialProfile, string awsProfile, CredentialsConfiguration credentialsConfig) { var authResult = await loginService.InteractiveLogin(credentialsConfig.OktaProfile!); @@ -74,7 +74,7 @@ Do you want renew the credentials now ?[/] var awsCredentialsData = await _awsCredentialsService.GetAwsCredentials(samlData.SamlAssertion, credentialsConfig.RoleArn, idp); - _awsCredentialsService.StoreCredentials(awsProfileName, awsCredentialsData); + _awsCredentialsService.StoreCredentials(awsProfile, awsCredentialsData); return awsCredentialsData; } @@ -99,6 +99,13 @@ [bold yellow]The AWS role ARN specified in the credential [b]'{credentialProfile return null; } - private static SessionAWSCredentials CreateAwsCredentials(AwsCredentialsData credentialsData) => - new(credentialsData.AccessKeyId, credentialsData.SecretAccessKey, credentialsData.SessionToken); + private SessionAWSCredentials CreateAwsCredentials(AwsCredentialsData credentialsData, string credentialProfile, string? outputAwsProfile) + { + if (outputAwsProfile is not null && outputAwsProfile != credentialProfile) + { + _awsCredentialsService.StoreCredentials(outputAwsProfile, credentialsData); + } + + return new SessionAWSCredentials(credentialsData.AccessKeyId, credentialsData.SecretAccessKey, credentialsData.SessionToken); + } } diff --git a/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/CredentialsManager.cs b/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/CredentialsManager.cs index 44b6e44..8371c13 100644 --- a/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/CredentialsManager.cs +++ b/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/CredentialsManager.cs @@ -8,7 +8,7 @@ namespace Ellosoft.AwsCredentialsManager.Services.Configuration.Interactive; public class CredentialsManager(IConfigManager configManager) { - public string GetCredential() + public string GetCredentialNameFromUser() { var appConfig = configManager.AppConfig; @@ -43,7 +43,7 @@ public bool TryGetCredential(string credentialProfile, [NotNullWhen(true)] out C return false; } - public void CreateCredential(string name, string awsProfile, string awsRole, string oktaAppUrl, string oktaProfile) + public void CreateCredential(string name, string? awsProfile, string awsRole, string oktaAppUrl, string oktaProfile) { var credential = new CredentialsConfiguration { diff --git a/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/EnvironmentManager.cs b/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/EnvironmentManager.cs index dc09d40..72b2408 100644 --- a/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/EnvironmentManager.cs +++ b/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Interactive/EnvironmentManager.cs @@ -54,7 +54,7 @@ private EnvironmentConfiguration CreateNewEnvironment(string? environment = null AnsiConsole.MarkupLine($"Creating environment [green i]{environmentName}[/]"); - var credentialName = _credentialsManager.GetCredential(); + var credentialName = _credentialsManager.GetCredentialNameFromUser(); var environmentConfig = new EnvironmentConfiguration { Credential = credentialName }; while (!_configManager.AppConfig.Environments.TryAdd(environmentName, environmentConfig)) diff --git a/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Models/CredentialsConfiguration.cs b/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Models/CredentialsConfiguration.cs index eff7852..f73e321 100644 --- a/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Models/CredentialsConfiguration.cs +++ b/src/Ellosoft.AwsCredentialsManager/Services/Configuration/Models/CredentialsConfiguration.cs @@ -6,9 +6,14 @@ public class CredentialsConfiguration : ResourceConfiguration { public required string RoleArn { get; set; } - public required string AwsProfile { get; set; } = "default"; + public string? AwsProfile { get; set; } public string? OktaAppUrl { get; set; } public string? OktaProfile { get; set; } + + /// + /// If the AwsProfile is populated then return its value otherwise returns the credential name + /// + internal string GetAwsProfileSafe(string credentialName) => AwsProfile ?? credentialName; }