예제 #1
0
        private static async Task <GetAuthorizationTokenResponse> GetAuthorizationToken(string accessKeyId, string secretKey)
        {
            Output.Verbose($"Retrieving authorization token {nameof(accessKeyId)}={accessKeyId}, {nameof(secretKey)}={secretKey}.");

            GetAuthorizationTokenResponse authorizationToken;

            // We only want to get & save secrets from/to local configuration when we have not provided them
            var needToStoreSecrets = false;

            if (accessKeyId == null || secretKey == null)
            {
                Output.Verbose("Attempting to load AWS credentials from secrets store.");
                var securityConfig = GetConfiguration();
                if (accessKeyId == null)
                {
                    accessKeyId = securityConfig[SecretStoreKeys.AccessKeyId];
                }
                if (secretKey == null)
                {
                    secretKey = securityConfig[SecretStoreKeys.SecretKey];
                }
            }

            var firstPrompt = true;

            do
            {
                if (accessKeyId == null || secretKey == null)
                {
                    Output.Verbose("Attempting to prompt for AWS credentials.");

                    needToStoreSecrets = true;
                    if (!ConsoleHelper.IsInteractive)
                    {
                        // CLI is running from within a script in a non-interactive terminal, e.g., from pressing F5 in VS Code. The
                        // auth command will get users to this same code path, but in an interactive terminal.

                        var cliCommand  = Program.CliCommandName;
                        var authCommand = new AuthorizeCommand().CommandName;

                        Output.Error(Output.HorizontalLine());
                        Output.Error("Your machine is missing necessary AWS credentials. Authorize your machine by opening a terminal and " +
                                     "running the following commands, then try your actions again. This authorization only needs to be performed once per user/machine:");
                        Output.Error($"> cd {Directory.GetCurrentDirectory()}");
                        Output.Error($"> dotnet {cliCommand} {authCommand}");
                        Output.Error($"If you have further issues, please contact #watch-ops.");
                        Output.Error(Output.HorizontalLine());

                        return(null);
                    }

                    // TODO add link to docs page for finding/creating AWS access keys and getting help from watch-ops to set up account
                    // or add permissions.
                    if (firstPrompt)
                    {
                        Output.Error("Your machine is missing necessary AWS credentials. The following authorization only needs to be performed " +
                                     "once per user/machine. For assistance, please contact #watch-ops.");
                        firstPrompt = false;
                    }
                    Output.Error("Paste in your AWS ACCESS KEY ID:");
                    accessKeyId = ConsoleHelper.ReadPassword();

                    Output.Error("Now paste in your AWS SECRET ACCESS KEY:");
                    secretKey = ConsoleHelper.ReadPassword();
                }

                try
                {
                    var ecrClient = new AmazonECRClient(accessKeyId, secretKey, _regionEndpoint);

                    // TODO Potentially slow. We might need to create a cached toked since it'll be valid for 12  hours
                    // Another thing is that there is a throttling for GetAuthorizationTokenAsync (1 call / second)
                    // There's not a good way to check the stored credentials without making
                    // an actual call to ECR (e.g. a docker pull). Doing that on every operation
                    // would be relatively slow. Could we write a hidden file on successful auth
                    // check and expire it after 8 hours?
                    Output.Verbose($"Retrieving ECR authentication token");
                    authorizationToken = await ecrClient.GetAuthorizationTokenAsync(new GetAuthorizationTokenRequest(),
                                                                                    new CancellationTokenSource(_awsTimeout).Token);


                    // We have successfully got authorization token let's save our access key and secret in user-secrets.
                    // We only save them when we've not provided access and secret via parameters.
                    if (needToStoreSecrets)
                    {
                        Output.Verbose($"Storing secret {SecretStoreKeys.AccessKeyId}");
                        await SaveSecret(SecretStoreKeys.AccessKeyId, accessKeyId);

                        Output.Verbose($"Storing secret {SecretStoreKeys.SecretKey}");
                        await SaveSecret(SecretStoreKeys.SecretKey, secretKey);
                    }
                    break;
                }
                catch (Exception e)
                {
                    var amazonEcrException = e.InnerException as AmazonECRException;
                    if (amazonEcrException != null && amazonEcrException.ErrorCode.Equals("UnrecognizedClientException"))
                    {
                        Output.VerboseException(e);
                        Output.Error("Bad credentials. Please try again.");
                        accessKeyId = null;
                        secretKey   = null;
                        continue;
                    }

                    // TODO handle case where permissions are inadequate (?)
                    Output.Error("Something went wrong while connecting to AWS. Please try again later.");
                    Output.Exception(e);
                    throw;
                }
            } while (true);
            return(authorizationToken);
        }