36
36
import urllib2
37
37
import urlparse
38
38
39
- try :
40
- from urlparse import parse_qs , parse_qsl
41
- except ImportError :
42
- from cgi import parse_qs , parse_qsl
43
-
44
39
try :
45
40
import simplejson
46
41
except ImportError :
@@ -65,29 +60,32 @@ def __str__(self):
65
60
66
61
class Client (object ):
67
62
""" Client for OAuth 2.0 'Bearer Token' """
63
+ redirect_uri = None
64
+ auth_uri = None
65
+ refresh_uri = None
66
+ user_agent = None
67
+ scope = None
68
68
69
- def __init__ (self , client_id , client_secret , redirect_uri = None ,
70
- timeout = None ):
71
- self .client_id = client_id
72
- self .client_secret = client_secret
73
- self .redirect_uri = redirect_uri
74
- self .timeout = timeout
69
+ def __init__ (self , client_id , client_secret , access_token = None ,
70
+ refresh_token = None , timeout = None ):
75
71
76
- if self . client_id is None or self . client_secret is None :
72
+ if not client_id or not client_secret :
77
73
raise ValueError ("Client_id and client_secret must be set." )
78
74
79
- @staticmethod
80
- def _split_url_string (param_str ):
81
- """Turn URL string into parameters."""
82
- parameters = parse_qs (param_str , keep_blank_values = False )
83
- for key , val in parameters .iteritems ():
84
- parameters [key ] = urllib .unquote (val [0 ])
85
- return parameters
75
+ self .client_id = client_id
76
+ self .client_secret = client_secret
77
+ self .timeout = timeout
78
+ self .access_token = access_token
79
+ self .refresh_token = refresh_token
86
80
87
- def authorization_url (self , uri , redirect_uri = None , scope = None ):
81
+ def authorization_url (self , auth_uri = None , redirect_uri = None , scope = None ):
88
82
""" Get the URL to redirect the user for client authorization """
89
83
if redirect_uri is None :
90
84
redirect_uri = self .redirect_uri
85
+ if auth_uri is None :
86
+ auth_uri = self .auth_uri
87
+ if scope is None :
88
+ scope = self .scope
91
89
92
90
params = {'client_id' : self .client_id ,
93
91
'redirect_uri' : redirect_uri ,
@@ -96,74 +94,127 @@ def authorization_url(self, uri, redirect_uri=None, scope=None):
96
94
if scope :
97
95
params ['scope' ] = scope
98
96
99
- return '%s?%s' % (uri , urllib .urlencode (params ))
97
+ return '%s?%s' % (auth_uri , urllib .urlencode (params ))
100
98
101
- def access_token (self , uri , redirect_uri = None , code = None , scope = None ):
99
+ def redeem_code (self , refresh_uri = None , redirect_uri = None , code = None , scope = None ):
102
100
"""Get an access token from the supplied code """
103
101
104
102
# prepare required args
105
103
if code is None :
106
104
raise ValueError ("Code must be set." )
107
105
if redirect_uri is None :
108
106
redirect_uri = self .redirect_uri
107
+ if refresh_uri is None :
108
+ refresh_uri = self .refresh_uri
109
+ if scope is None :
110
+ scope = self .scope
111
+
109
112
data = {
110
113
'client_id' : self .client_id ,
111
114
'client_secret' : self .client_secret ,
112
115
'code' : code ,
113
116
'redirect_uri' : redirect_uri ,
114
117
'grant_type' : 'authorization_code' ,
115
118
}
119
+
116
120
if scope is not None :
117
121
data ['scope' ] = scope
118
122
body = urllib .urlencode (data )
119
123
120
- headers = {'content -type' : 'application/x-www-form-urlencoded' ,
121
- ' user_agent' : 'HiveFire-OAuth2a' ,
122
- }
124
+ headers = {'Content -type' : 'application/x-www-form-urlencoded' }
125
+ if self . user_agent :
126
+ headers [ 'user-agent' ] = self . user_agent
123
127
124
- response = self .request ( uri , body = body , method = 'POST' , headers = headers )
125
- if not response .code = = 200 :
128
+ response = self ._request ( refresh_uri , body = body , method = 'POST' , headers = headers )
129
+ if response .code ! = 200 :
126
130
raise Error (response .read ())
127
131
response_args = simplejson .loads (response .read ())
128
132
129
133
error = response_args .pop ('error' , None )
130
134
if error is not None :
131
135
raise Error (error )
132
136
133
- return response_args ['access_token' ], response_args ['refresh_token' ]
137
+ self .access_token = response_args ['access_token' ]
138
+ self .refresh_token = response_args ['refresh_token' ]
139
+ return self .access_token , self .refresh_token
134
140
135
- def refresh (self , uri , refresh_token , secret_type = None ):
141
+ def refresh_access_token (self , refresh_uri = None , refresh_token = None ):
136
142
""" Get a new access token from the supplied refresh token """
137
143
144
+ if refresh_uri is None :
145
+ refresh_uri = self .refresh_uri
138
146
if refresh_token is None :
139
- raise ValueError ( "Refresh_token must be set." )
147
+ refresh_token = self . refresh_token
140
148
141
149
# prepare required args
142
150
args = {
143
- 'type' : 'refresh' ,
144
151
'client_id' : self .client_id ,
145
152
'client_secret' : self .client_secret ,
146
153
'refresh_token' : refresh_token ,
154
+ 'grant_type' : 'refresh_token' ,
147
155
}
148
-
149
- # prepare optional args
150
- if secret_type is not None :
151
- args ['secret_type' ] = secret_type
152
-
153
156
body = urllib .urlencode (args )
154
- headers = {
155
- 'Content-Type' : 'application/x-www-form-urlencoded' ,
156
- }
157
157
158
- response = self .request (uri , method = 'POST' , body = body , headers = headers )
159
- if not response .code == 200 :
158
+ headers = {'Content-type' : 'application/x-www-form-urlencoded' }
159
+ if self .user_agent :
160
+ headers ['user-agent' ] = self .user_agent
161
+
162
+ response = self ._request (refresh_uri , method = 'POST' , body = body , headers = headers )
163
+ if response .code != 200 :
160
164
raise Error (response .read ())
165
+ response_args = simplejson .loads (response .read ())
161
166
162
- response_args = Client ._split_url_string (content )
163
- return response_args
167
+ self .access_token = response_args ['access_token' ]
168
+ # server may or may not supply a new refresh token
169
+ self .refresh_token = response_args .get ('refresh_token' , self .refresh_token )
170
+ return self .access_token , self .refresh_token
164
171
165
- def request (self , uri , body = None , headers = None , method = 'GET' ):
166
- if method == 'POST' and body is None :
172
+ def _request (self , uri , body = None , headers = None , method = 'GET' ):
173
+ if method == 'POST' and not body :
167
174
raise ValueError ('POST requests must have a body' )
175
+
168
176
request = urllib2 .Request (uri , body , headers )
169
177
return urllib2 .urlopen (request , timeout = self .timeout )
178
+
179
+ def request (self , uri , body , headers , method = 'GET' ):
180
+ """ perform a HTTP request using OAuth authentication.
181
+ If the request fails because the access token is expired it will
182
+ try to refresh the token and try the request again.
183
+ """
184
+ headers ['Authorization' ] = 'Bearer %s' % self .access_token
185
+
186
+ try :
187
+ response = self ._request (uri , body = body , headers = headers , method = method )
188
+ except urllib2 .HTTPError as e :
189
+ if 400 <= e .code < 500 :
190
+ # any 400 code is acceptable to signal that the access token is expired.
191
+ self .refresh_access_token ()
192
+ headers ['Authorization' ] = 'Bearer %s' % self .access_token
193
+ response = self ._request (uri , body = body , headers = headers , method = method )
194
+
195
+ if response .code == 200 :
196
+ return simplejson .loads (response .read ())
197
+ raise ValueError (response .read ())
198
+
199
+ class GooglAPI (Client ):
200
+ user_agent = 'python-foauth2'
201
+ # OAuth API
202
+ auth_uri = 'https://accounts.google.com/o/oauth2/auth'
203
+ refresh_uri = 'https://accounts.google.com/o/oauth2/token'
204
+ scope = 'https://www.googleapis.com/auth/urlshortener'
205
+ # data API
206
+ api_uri = 'https://www.googleapis.com/urlshortener/v1/url'
207
+
208
+ def shorten (self , long_url ):
209
+ data = simplejson .dumps ({'longUrl' : long_url })
210
+ headers = {'Content-Type' : 'application/json' }
211
+ json_d = self .request (self .api_uri , data , headers , 'POST' )
212
+ return json_d ['id' ]
213
+
214
+ def stats (self , short_url ):
215
+ params = {'shortUrl' : short_url ,
216
+ 'projection' : 'ANALYTICS_CLICKS' ,
217
+ }
218
+ stat_url = self .api_uri + '&' + urllib .urlencode (params )
219
+ headers = {'Content-Type' : 'application/json' }
220
+ return self .request (stat_url , None , headers )
0 commit comments