forked from aidan-/httpie-aws-authv4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
httpie_aws_authv4.py
126 lines (101 loc) · 4.24 KB
/
httpie_aws_authv4.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# -*- coding: utf-8 -*-
"""
AWS-v4 auth plugin for HTTPie.
"""
import re
import io
from httpie.plugins import AuthPlugin
from aws_requests_auth.aws_auth import AWSRequestsAuth
from boto3 import session
from urllib3.util import parse_url
__version__ = '0.1.4'
__author__ = 'Aidan Rowe'
__licence__ = 'BSD'
class AWSAuth(object):
def __init__(self, access_key=None, secret_key=None, domain=None, profile=None):
self.domain = domain
if access_key and secret_key:
self.aws_access_key = access_key
self.aws_secret_access_key = secret_key
self.aws_token = None
else:
sess = session.Session(profile_name=profile)
creds = sess.get_credentials()
self.aws_access_key = creds.access_key
self.aws_secret_access_key = creds.secret_key
self.aws_token = creds.token
def __call__(self, r):
try:
# Host used in signature *MUST* always match with Host HTTP header.
host = r.headers.get('Host')
if not host:
_, _, host, _, _, _, _ = parse_url(r.url)
r.headers['Host'] = host
if self.domain is not None:
aws_params = self._parse_url(self.domain)
else:
aws_params = self._parse_url(host)
except ValueError:
print("ERROR: Could not parse neccessary information from URL.")
raise
except Exception as error:
print("Error parsing URL: %s" % error)
raise
if isinstance(r.body, io.RawIOBase):
r.body = r.body.readall()
elif isinstance(r.body, (io.BufferedIOBase, io.TextIOBase)):
r.body = r.body.read()
aws_request = AWSRequestsAuth(aws_access_key=self.aws_access_key,
aws_secret_access_key=self.aws_secret_access_key,
aws_host=host,
aws_region=aws_params['region'],
aws_service=aws_params['service'],
aws_token=self.aws_token)
return aws_request.__call__(r)
@staticmethod
def _parse_url(domain):
m = re.search(r"([^.]+)\.es\.amazonaws\.com$", domain)
if m:
return {"region": m.group(1),
"service": "es"}
m = re.search(r"([^.]+)\.([^.]+)\.amazonaws\.com$", domain)
if m:
return {"region": m.group(2),
"service": m.group(1)}
raise ValueError("Could not determine AWS region or service from domain name.")
class AWSv4AuthPlugin(AuthPlugin):
name = 'AWS auth-v4'
auth_type = 'aws4'
description = 'Sign requests using the AWS Signature Version 4 Signing Process'
auth_require = False
auth_parse = False
prompt_password = False
def get_auth(self, username=None, password=None):
# To remain consistant with AWS tools, boto3 credential store is used
# to retrieve the users API keys. This means environment variables
# take precedent over ~/.aws/credentials and IAM roles. credentials
# can be provided on the CLI using the -a flag
# eg, -a <access_key>:<secret_access_key>
# An attempt is made to try and determine the region, endpoint and
# service from a given request URL. If you are using a custom domain
# this is no doubt going fail and the user will need to input the neccessary
# information using the `-a` flag.
# The format for the `-a` flag is:
# "service:region:endpoint"
access_key = None
secret_key = None
domain = None
profile = None
if self.raw_auth is not None:
parts = self.raw_auth.split(':')
if len(parts) >= 2:
if parts[0] == 'profile':
profile = parts[1]
else:
access_key = parts[0]
secret_key = parts[1]
if len(parts) == 3:
domain = parts[2]
elif len(parts) == 1:
domain = parts[0]
return AWSAuth(access_key=access_key, secret_key=secret_key, domain=domain, profile=profile)