Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print mostly to stderr so users can source the export line #207

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions aws_google_auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
#!/usr/bin/env python
from __future__ import print_function

import argparse
import base64
import os
import sys
import logging

import keyring
from six import print_ as print
from tzlocal import get_localzone

from aws_google_auth import _version
Expand Down Expand Up @@ -77,7 +74,7 @@ def cli(cli_args):
config = resolve_config(args)
process_auth(args, config)
except google.ExpectedGoogleException as ex:
print(ex)
logging.error(ex)
sys.exit(1)
except KeyboardInterrupt:
pass
Expand Down Expand Up @@ -274,8 +271,8 @@ def process_auth(args, config):
else:
config.role_arn, config.provider = util.Util.pick_a_role(roles)
if not config.quiet:
print("Assuming " + config.role_arn)
print("Credentials Expiration: " + format(amazon_client.expiration.astimezone(get_localzone())))
util.Util.message("Assuming " + config.role_arn)
util.Util.message("Credentials Expiration: " + format(amazon_client.expiration.astimezone(get_localzone())))

if config.print_creds:
amazon_client.print_export_line()
Expand Down
2 changes: 2 additions & 0 deletions aws_google_auth/amazon.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python
from __future__ import print_function

import base64
import boto3
Expand Down Expand Up @@ -72,6 +73,7 @@ def print_export_line(self):
self.session_token,
self.expiration.strftime('%Y-%m-%dT%H:%M:%S%z'))

# Print to stdout (not stderr) so the output can be sourced by a shell.
print(formatted)

@property
Expand Down
40 changes: 18 additions & 22 deletions aws_google_auth/google.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function

import base64
import io
import json
Expand All @@ -15,10 +13,10 @@
from distutils.spawn import find_executable
from bs4 import BeautifulSoup
from requests import HTTPError
from six import print_ as print
from six.moves import urllib_parse, input
from six.moves import urllib_parse

from aws_google_auth import _version
from aws_google_auth.util import Util

# The U2F USB Library is optional, if it's there, include it.
try:
Expand Down Expand Up @@ -377,7 +375,7 @@ def handle_captcha(self, sess, payload):
if find_executable('xv') is None and find_executable('display') is None:
open_image = False

print("Please visit the following URL to view your CAPTCHA: {}".format(captcha_url))
Util.message("Please visit the following URL to view your CAPTCHA: {}".format(captcha_url))

if open_image:
try:
Expand All @@ -387,10 +385,7 @@ def handle_captcha(self, sess, payload):
except Exception:
pass

try:
captcha_input = raw_input("Captcha (case insensitive): ") or None
except NameError:
captcha_input = input("Captcha (case insensitive): ") or None
captcha_input = Util.get_input("Captcha (case insensitive): ")

# Update the payload
payload['identifier-captcha-input'] = captcha_input
Expand Down Expand Up @@ -475,7 +470,7 @@ def handle_sk(self, sess):
if attempts_remaining <= 0:
break
else:
input(
Util.get_input(
"Insert your U2F device and press enter to try again..."
)
attempts_remaining -= 1
Expand Down Expand Up @@ -531,7 +526,7 @@ def handle_sms(self, sess):
response_page = BeautifulSoup(sess.text, 'html.parser')
challenge_url = sess.url.split("?")[0]

sms_token = input("Enter SMS token: G-") or None
sms_token = Util.get_input("Enter SMS token: G-") or None

payload = {
'challengeId':
Expand Down Expand Up @@ -597,7 +592,7 @@ def handle_prompt(self, sess):

self.check_prompt_code(response_page)

print("Open the Google App, and tap 'Yes' on the prompt to sign in ...")
Util.message("Open the Google App, and tap 'Yes' on the prompt to sign in ...")

self.session.headers['Referer'] = sess.url

Expand Down Expand Up @@ -673,7 +668,7 @@ def check_prompt_code(response):
"""
num_code = response.find("div", {"jsname": "EKvSSd"})
if num_code:
print("numerical code for prompt: {}".format(num_code.string))
Util.message("numerical code for prompt: {}".format(num_code.string))

def handle_totp(self, sess):
response_page = BeautifulSoup(sess.text, 'html.parser')
Expand All @@ -682,7 +677,7 @@ def handle_totp(self, sess):
challenge_url = sess.url.split("?")[0]
challenge_id = challenge_url.split("totp/")[1]

mfa_token = input("MFA token: ") or None
mfa_token = Util.get_input("MFA token: ") or None

if not mfa_token:
raise ValueError(
Expand All @@ -709,12 +704,12 @@ def handle_totp(self, sess):
def handle_iap(self, sess):
response_page = BeautifulSoup(sess.text, 'html.parser')
challenge_url = sess.url.split("?")[0]
phone_number = input('Enter your phone number:') or None
phone_number = Util.get_input('Enter your phone number:') or None

while True:
try:
choice = int(
input(
Util.get_input(
'Type 1 to receive a code by SMS or 2 for a voice call:'
))
if choice not in [1, 2]:
Expand Down Expand Up @@ -778,7 +773,7 @@ def handle_iap(self, sess):
response_page = BeautifulSoup(sess.text, 'html.parser')
challenge_url = sess.url.split("?")[0]

token = input("Enter " + send_method + " token: G-") or None
token = Util.get_input("Enter " + send_method + " token: G-") or None

payload = {
'challengeId':
Expand Down Expand Up @@ -858,20 +853,21 @@ def handle_selectchallenge(self, sess):
if k in auth_methods and k not in unavailable_challenge_ids
}

print('Choose MFA method from available:')
print('\n'.join(
Util.message('Choose MFA method from available:')
Util.message('\n'.join(
'{}: {}'.format(*i) for i in list(auth_methods.items())))

selected_challenge = input("Enter MFA choice number ({}): ".format(
challenge_ids[-1:][0])) or None
selected_challenge = Util.get_input(
"Enter MFA choice number ({}): ".format(
challenge_ids[-1:][0])) or None

if selected_challenge is not None and int(selected_challenge) in challenge_ids:
challenge_id = int(selected_challenge)
else:
# use the highest index as that will default to prompt, then sms, then totp, etc.
challenge_id = challenge_ids[-1:][0]

print("MFA Type Chosen: {}".format(auth_methods[challenge_id]))
Util.message("MFA Type Chosen: {}".format(auth_methods[challenge_id]))

# We need the specific form of the challenge chosen
challenge_form = response_page.find(
Expand Down
7 changes: 5 additions & 2 deletions aws_google_auth/u2f.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from u2flib_host import u2f, exc, appid
from u2flib_host.constants import APDU_USE_NOT_SATISFIED

from aws_google_auth.util import Util

"""
The facet/appID used by Google auth does not seem to be valid
Need to apply some patches to u2flib_host to not validate the
Expand Down Expand Up @@ -69,8 +71,9 @@ def u2f_auth(challenges, facet):
if e.code == APDU_USE_NOT_SATISFIED:
remove = False
if not prompted:
print('Touch the flashing U2F device to '
'authenticate...')
Util.message(
'Touch the flashing U2F device to '
'authenticate...')
prompted = True
else:
pass
Expand Down
21 changes: 13 additions & 8 deletions aws_google_auth/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class Util:

@staticmethod
def get_input(prompt):
return input(prompt)
Util.message(prompt, end="")
return input()

@staticmethod
def pick_a_role(roles, aliases=None, account=None):
Expand Down Expand Up @@ -43,26 +44,26 @@ def pick_a_role(roles, aliases=None, account=None):
enriched_roles_tab.append([i + 1, role_property[0], role_property[1]])

while True:
print(tabulate(enriched_roles_tab, headers=['No', 'AWS account', 'Role'], ))
Util.message(tabulate(enriched_roles_tab, headers=['No', 'AWS account', 'Role'], ))
prompt = 'Type the number (1 - {:d}) of the role to assume: '.format(len(enriched_roles))
choice = Util.get_input(prompt)

try:
return list(ordered_roles.items())[int(choice) - 1]
except (IndexError, ValueError):
print("Invalid choice, try again.")
Util.message("Invalid choice, try again.")
else:
while True:
for i, role in enumerate(filtered_roles):
print("[{:>3d}] {}".format(i + 1, role))
Util.message("[{:>3d}] {}".format(i + 1, role))

prompt = 'Type the number (1 - {:d}) of the role to assume: '.format(len(filtered_roles))
choice = Util.get_input(prompt)

try:
return list(filtered_roles.items())[int(choice) - 1]
except (IndexError, ValueError):
print("Invalid choice, try again.")
Util.message("Invalid choice, try again.")

@staticmethod
def touch(file_name, mode=0o600):
Expand Down Expand Up @@ -95,8 +96,12 @@ def get_password(prompt):
if sys.stdin.isatty():
password = getpass.getpass(prompt)
else:
print(prompt, end="")
sys.stdout.flush()
Util.message(prompt, end="")
password = sys.stdin.readline()
print("")
Util.message("")
return password

@staticmethod
def message(*msg, **print_kwargs):
"""Print a user-interaction message to stderr."""
print(*msg, file=sys.stderr, **print_kwargs)