16
16
17
17
from six .moves import urllib
18
18
import argparse
19
- from oauth2client .client import GoogleCredentials
20
19
import time
21
-
22
-
23
- def sign_string (credentials , s ):
24
- # TODO add IAM workaround, will raise NotImplementedError in GCE
25
- return credentials .sign_blob (s )
20
+ import httplib2
21
+ import base64
22
+
23
+
24
+ def sign_gae (b ):
25
+ from oauth2client .contrib import appengine
26
+ creds = appengine .AppAssertionCredentials ([])
27
+ return creds .service_account_email , creds .sign_blob (b )[1 ]
28
+
29
+
30
+ def sign_from_file (filename , b ):
31
+ from oauth2client .service_account import ServiceAccountCredentials
32
+ creds = ServiceAccountCredentials .from_json_keyfile_name (filename )
33
+ return creds .service_account_email , creds .sign_blob (b )[1 ]
34
+
35
+
36
+ def sign_gce (b ):
37
+ from googleapiclient .discovery import build
38
+ from oauth2client .contrib import gce
39
+
40
+ creds = gce .AppAssertionCredentials ()
41
+ iam = build ('iam' , 'v1' , credentials = creds )
42
+ resp = iam .projects ().serviceAccounts ().signBlob (
43
+ name = "projects/{project_id}/serviceAccounts/{sa_email}" .format (
44
+ project_id = _project_id_from_metadata (),
45
+ sa_email = creds .service_account_email
46
+ ),
47
+ body = {
48
+ "bytesToSign" : b
49
+ }
50
+ ).execute ()
51
+ return creds .service_account_email , resp ['signature' ]
52
+
53
+
54
+ def _project_id_from_metadata ():
55
+ http_request = httplib2 .Http ().request
56
+ response , content = http_request (
57
+ ('http://metadata.google.internal/computeMetadata/'
58
+ 'v1/project/project-id' ),
59
+ headers = {'Metadata-Flavor' : 'Google' }
60
+ )
61
+ return None , content .decode ('utf-8' )
26
62
27
63
28
64
def make_signature_string (method ,
@@ -36,7 +72,7 @@ def make_signature_string(method,
36
72
method ,
37
73
content ,
38
74
content_type ,
39
- expiration ,
75
+ str ( expiration ) ,
40
76
extension_header_string ,
41
77
resource
42
78
])
@@ -60,7 +96,7 @@ def make_resource_string(parsed):
60
96
def make_header_string (** custom_headers ):
61
97
return '\n ' .join ([
62
98
':' .join ([header .lower (), value .lower ()])
63
- for header , value in custom_headers .iteritems (). sort ( )
99
+ for header , value in sorted ( custom_headers .items () )
64
100
])
65
101
66
102
@@ -69,53 +105,67 @@ def main(url=None,
69
105
content_file = None ,
70
106
content_type = '' ,
71
107
method = 'GET' ,
108
+ credentials = None ,
72
109
** kwargs ):
73
110
parsed = urllib .parse .urlparse (url )
74
111
75
- credentials = GoogleCredentials .get_application_default ()
76
-
77
112
if content_file :
78
113
with open (content_file , 'r' ) as content_f :
79
114
content = content_f .read ()
80
115
else :
81
116
content = ''
82
117
83
- expiration = time .time () + duration
118
+ expiration = int ( time .time () + duration )
84
119
85
120
signature_string = make_signature_string (
86
121
method ,
87
122
make_resource_string (parsed ),
88
123
expiration ,
89
- extension_header_stirng = make_header_string (** headers ),
124
+ extension_header_string = make_header_string (** headers ),
90
125
content = content ,
91
126
content_type = content_type
92
127
)
93
128
94
- signed_string = sign_string (credentials , signature_string )
129
+ print (signature_string )
130
+
131
+ if credentials == 'gae' :
132
+ email , signed_string = sign_gae (signature_string )
133
+ elif credentials == 'gce' :
134
+ email , signed_string = sign_gce (signature_string )
135
+ else :
136
+ email , signed_string = sign_from_file (credentials , signature_string )
137
+
138
+ sig_bytes = base64 .b64encode (signed_string )
95
139
96
140
query_params = urllib .parse .parse_qs (parsed .query )
97
141
query_params .update (
98
- GoogleAccessId = credentials . email ,
142
+ GoogleAccessId = email ,
99
143
Expires = expiration ,
100
- Signature = signed_string
144
+ Signature = sig_bytes
101
145
)
102
- new_query_string = urllib . parse . urlencode ( query_params )
103
- parsed [ 3 ] = new_query_string
146
+ url_tuple = list ( parsed )
147
+ url_tuple [ 4 ] = urllib . parse . urlencode ( query_params , doseq = True )
104
148
105
- print (urllib .parse .urlunparse (parsed ))
149
+ print (urllib .parse .urlunparse (url_tuple ))
106
150
107
151
108
152
if __name__ == '__main__' :
109
153
parser = argparse .ArgumentParser ('Arguments for signing a url' )
110
- parser .add_argument ('url' , required = True , help = 'A fully qualified url' )
154
+ parser .add_argument ('url' , help = 'A fully qualified url' )
111
155
parser .add_argument (
112
156
'--method' ,
113
157
default = 'GET' ,
114
158
help = 'HTTP Method for the request'
115
159
)
160
+ parser .add_argument (
161
+ '--credentials' ,
162
+ required = True ,
163
+ help = "Either the string \" gae\" the string \" gce\" or a filename"
164
+ )
116
165
parser .add_argument ('--content-file' , help = 'File name for request content' )
117
166
parser .add_argument (
118
167
'--content-type' ,
168
+ default = '' ,
119
169
help = 'MIME type for the content'
120
170
)
121
171
parser .add_argument (
0 commit comments