forked from gnubila-france/pyrax-identity-hubic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpyrax_identity_hubic.py
257 lines (210 loc) · 9.04 KB
/
pyrax_identity_hubic.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Gu1
# Licensed under the MIT license
import pyrax
import pyrax.exceptions as exc
import requests
import re
import urlparse
import ConfigParser
import time
from pyrax.base_identity import BaseIdentity, Service
from requests.compat import quote, quote_plus
OAUTH_ENDPOINT = "https://api.hubic.com/oauth/"
API_ENDPOINT = "https://api.hubic.com/1.0/"
class BearerTokenAuth(requests.auth.AuthBase):
def __init__(self, token):
self.token = token
def __call__(self, req):
req.headers['Authorization'] = 'Bearer '+self.token
return req
class HubicIdentity(BaseIdentity):
def _get_auth_endpoint(self):
return ""
def set_credentials(self, email, password, client_id,
client_secret, redirect_uri,
authenticate=False):
"""Sets the username and password directly."""
self._email = email
self._password = password
self._client_id = client_id
self.tenant_id = client_id
self._client_secret = client_secret
self._redirect_uri = redirect_uri
if authenticate:
self.authenticate()
def _read_credential_file(self, cfg):
"""
Parses the credential file with Rackspace-specific labels.
"""
self._email = cfg.get("hubic", "email")
self._password = cfg.get("hubic", "password")
self._client_id = cfg.get("hubic", "client_id")
self.tenant_id = self._client_id
self._client_secret = cfg.get("hubic", "client_secret")
self._redirect_uri = cfg.get("hubic", "redirect_uri")
def _parse_error(self, resp):
if not 'location' in resp.headers:
return None
query = urlparse.urlsplit(resp.headers['location']).query
qs = dict(urlparse.parse_qsl(query))
return {'error': qs['error'], 'error_description': qs['error_description']}
def _get_access_token(self, code):
print "_get_access_token()"
r = requests.post(
OAUTH_ENDPOINT+'token/',
data={
'code': code,
'redirect_uri': self._redirect_uri,
'grant_type': 'authorization_code',
},
auth=(self._client_id, self._client_secret)
)
if r.status_code != 200:
try:
err = r.json()
err['code'] = r.status_code
except:
err = {}
raise exc.AuthenticationFailed("Unable to get oauth access token, "
"wrong client_id or client_secret ? (%s)"%str(err))
oauth_token = r.json()
config = ConfigParser.ConfigParser()
config.read('.pyrax-hubic.cfg')
if oauth_token['access_token'] is not None:
config.set("hubic", "access_token", oauth_token['access_token'])
with open('.pyrax-hubic.cfg', 'wb') as configfile:
config.write(configfile)
else:
raise exc.AuthenticationFailed("Unable to get oauth access token, wrong client_id or client_secret ? (%s)"%str(err))
if oauth_token['refresh_token'] is not None:
config.set("hubic", "refresh_token", oauth_token['refresh_token'])
with open('.pyrax-hubic.cfg', 'wb') as configfile:
config.write(configfile)
else:
raise exc.AuthenticationFailed("Unable to get the refresh token.")
# removing username and password from .pyrax-hubic.cfg
if config.has_option("hubic", "email"):
config.remove_option("hubic", "email")
with open('.pyrax-hubic.cfg', 'wb') as configfile:
config.write(configfile)
print "username has been removed from the .pyrax-hubic.cfg file sent to the CE."
if config.has_option("hubic", "password"):
config.remove_option("hubic", "password")
with open('.pyrax-hubic.cfg', 'wb') as configfile:
config.write(configfile)
print "password has been removed from the .pyrax-hubic.cfg file sent to the CE."
return oauth_token
def _refresh_access_token(self):
config = ConfigParser.ConfigParser()
config.read('.pyrax-hubic.cfg')
refresh_token = config.get("hubic", "refresh_token")
if refresh_token is None:
raise exc.AuthenticationFailed("refresh_token is null. Not acquiered before ?")
success = False
max_retries = 20
retries = 0
sleep_time = 30
max_sleep_time = 3600
while retries < max_retries and not success:
r = requests.post(
OAUTH_ENDPOINT+'token/',
data={
'refresh_token': refresh_token,
'grant_type': 'refresh_token',
},
auth=(self._client_id, self._client_secret)
)
if r.status_code != 200:
if r.status_code == 509:
print "status_code 509: attempt #", retries, " failed"
retries += 1
time.sleep(sleep_time)
sleep_time = sleep_time * 2
if sleep_time > max_sleep_time:
sleep_time = max_sleep_time
else:
try:
err = r.json()
err['code'] = r.status_code
except:
err = {}
raise exc.AuthenticationFailed("Unable to get oauth access token, wrong client_id or client_secret ? (%s)"%str(err))
else:
success = True
if not success:
raise exc.AuthenticationFailed("All the attempts failed to get the refresh token: status_code = 509: Bandwidth Limit Exceeded")
oauth_token = r.json()
if oauth_token['access_token'] is not None:
return oauth_token
else:
raise exc.AuthenticationFailed("Unable to get oauth access token from json")
def authenticate(self):
config = ConfigParser.ConfigParser()
config.read('.pyrax-hubic.cfg')
if config.has_option("hubic", "refresh_token"):
oauth_token = self._refresh_access_token()
else:
r = requests.get(
OAUTH_ENDPOINT+'auth/?client_id={0}&redirect_uri={1}'
'&scope=credentials.r,account.r&response_type=code&state={2}'.format(
quote(self._client_id),
quote_plus(self._redirect_uri),
pyrax.utils.random_ascii() # csrf ? wut ?..
),
allow_redirects=False
)
if r.status_code != 200:
raise exc.AuthenticationFailed("Incorrect/unauthorized "
"client_id (%s)"%str(self._parse_error(r)))
try:
from lxml import html as lxml_html
except ImportError:
lxml_html = None
if lxml_html:
oauth = lxml_html.document_fromstring(r.content).xpath('//input[@name="oauth"]')
oauth = oauth[0].value if oauth else None
else:
oauth = re.search(r'<input\s+[^>]*name=[\'"]?oauth[\'"]?\s+[^>]*value=[\'"]?(\d+)[\'"]?>', r.content)
oauth = oauth.group(1) if oauth else None
if not oauth:
raise exc.AuthenticationFailed("Unable to get oauth_id from authorization page")
if self._email is None or self._password is None:
raise exc.AuthenticationFailed("Cannot retrieve email and/or password. Please run expresslane-hubic-setup.sh")
r = requests.post(
OAUTH_ENDPOINT+'auth/',
data = {
'action': 'accepted',
'oauth': oauth,
'login': self._email,
'user_pwd': self._password,
'account': 'r',
'credentials': 'r',
},
allow_redirects=False
)
try:
query = urlparse.urlsplit(r.headers['location']).query
code = dict(urlparse.parse_qsl(query))['code']
except:
raise exc.AuthenticationFailed("Unable to authorize client_id, invalid login/password ?")
oauth_token = self._get_access_token(code)
if oauth_token['token_type'].lower() != 'bearer':
raise exc.AuthenticationFailed("Unsupported access token type")
r = requests.get(
API_ENDPOINT+'account/credentials',
auth=BearerTokenAuth(oauth_token['access_token']),
)
swift_token = r.json()
self.authenticated = True
self.token = swift_token['token']
self.expires = swift_token['expires']
self.services['object_store'] = Service(self, {
'name': 'HubiC',
'type': 'cloudfiles',
'endpoints': [
{'public_url': swift_token['endpoint']}
]
})
self.username = self.password = None