-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkas.py
322 lines (291 loc) · 10.5 KB
/
kas.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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
import hashlib
import json
from suds.client import Client
from suds.sudsobject import asdict
import logging
import socket
import getpass
import time
import os
class KAS:
'''
class to interact with the KAS API of the german hoster all-inkl.com,
see http://kasapi.kasserver.com/dokumentation/phpdoc/ for details
'''
WSDL_AUTH = "https://kasapi.kasserver.com/soap/wsdl/KasAuth.wsdl"
WSDL_API = "https://kasapi.kasserver.com/soap/wsdl/KasApi.wsdl"
_auth_token = ""
_user = ""
_client = ""
_flood_timestamp = dict()
def login(self, user, password, lifetime=5, update_lifetime=True,
debug=False):
'''
log into KAS, must be called before any other KAS operation
:param user: KAS user
:param password: password for KAS user
:param lifetime: session lifetime
:param update_lifetime: update lifetime on every request
:param debug: set to True to debug all KAS API calls
:return: nothing
'''
if debug:
logging.getLogger('suds.client').setLevel(logging.DEBUG)
self._user = user
client = Client(url=self.WSDL_AUTH)
request = {
'KasUser': self._user,
'KasAuthType': 'sha1',
'KasPassword': hashlib.sha1(password).hexdigest(),
'SessionLifeTime': lifetime,
'SessionUpdateLifeTime':
self._convert_bool_to_str(update_lifetime),
}
response = client.service.KasAuth(Params=json.dumps(request))
self._auth_token = response
self._client = Client(url=self.WSDL_API)
def _wait(self, function_name):
'''
waits blocking until given KAS action can be performed again
:param function_name: KAS action name
:return:
'''
if not function_name in self._flood_timestamp:
return
if time.time() > self._flood_timestamp[function_name]:
return
time.sleep(self._flood_timestamp[function_name] - time.time())
def _update_flood_protection_time(self, function_name, response):
'''
updates time after which given KAS action can be performed again to
avoid running into flooding protection
:param function_name:
:param response: response from KAS server
:return:
'''
self._flood_timestamp[function_name] = time.time() + self._get_flood_delay(response)
def _get_flood_delay(self, response):
'''
extracts flood delay from response
:param response: response from KAS
:return: delay
'''
return response.item[1].value.item[0].value
def _send_request(self, request):
'''
sends request to KAS server, waits if necassary to avoid running in flood protection
:param request: request to send
:return: response from KAS server
'''
function_name = request['KasRequestType']
self._wait(function_name)
response = self._client.service.KasApi(Params=json.dumps(request))
self._update_flood_protection_time(function_name, response)
return response
def _convert_str_to_bool(self, input):
'''
helper function which converts strings to True or False
:param input: string to convert
:return: True if input=="Y" or "TRUE", False if input=="N" or "FALSE"
'''
if input == "Y" or input == "TRUE":
return True
elif input == "N" or input == "FALSE":
return False
else:
raise ValueError(str(input) + " cannot be converted to bool")
def _convert_to_dict(self, data):
'''
function to convert SUDS responses to python dicts, this mangles list to one large dict
if you need a list of dicts this must be constructed outside of this function
:param data: response as given by SUDS
:return: dict with the values given in response, the types or the
arguments are converted to Int, Float or Bool as appropriate
'''
out = dict()
if isinstance(data, list):
out = dict()
for listelement in data:
out.update(self._convert_to_dict(listelement))
elif data.__class__.__name__ == "item":
if hasattr(data, "value"):
out = dict()
key = data.key
if (isinstance(key, list) and all(isinstance(element, basestring) for element in key)
and isinstance(data.value, list)):
key = "".join(key)
value = "".join(data.value).encode("unicode-escape")
out[key] = self._fix_type(value)
elif isinstance(key, basestring) and isinstance(data.value, basestring):
out[data.key] = self._fix_type(data.value.encode("unicode-escape"))
elif isinstance(key, basestring) and isinstance(data.value, int):
out[data.key] = data.value
elif isinstance(key, basestring) and data.value.__class__.__name__ == "value":
out[key] = self._convert_to_dict(data.value.item)
else:
raise ValueError("Invalid type")
else:
return self._convert_to_dict(data.item)
else:
raise ValueError("Invalid type")
return out
def _isnumeric(self, value):
'''
checks if string is a number
in contrast to python's own isnumeric and isdigit function this also
works for negative numbers
:param value: string to check
:return: True if value is a number, False otherwise
'''
try:
val = float(value)
return True
except ValueError:
return False
def _convert_str_to_numeric(self, s):
'''
:param s: string to convert
:return: Int or Float of s
'''
if not self._isnumeric(s):
raise ValueError(str(s) + " is no number")
try:
return int(s)
except ValueError:
return float(s)
def _fix_type(self, value):
'''
converts types given as string to bool, Int or Float as appropriate
:param data: string with value
:return: value converted to bool, Int or Float (if appropiate),
unchanged value otherwise
'''
if self._isnumeric(value):
return self._convert_str_to_numeric(value)
try:
return self._convert_str_to_bool(value)
except:
ValueError
pass
return value
def get_accounts(self):
'''
gets account data
:return: dict with account data
'''
request = {
'KasUser': self._user,
'KasAuthType': 'session',
'KasAuthData': self._auth_token,
'KasRequestType': "get_accounts",
'KasRequestParams': "",
}
response = self._send_request(request)
out = []
for listelement in response.item[1].value.item[2].value:
out.append(self._convert_to_dict(listelement))
return out
def get_accountsettings(self):
'''
gets account settings
:return: dict with account data
'''
request = {
'KasUser': self._user,
'KasAuthType': 'session',
'KasAuthData': self._auth_token,
'KasRequestType': "get_accountsettings",
'KasRequestParams': "",
}
response = self._send_request(request)
return self._convert_to_dict(response.item[1].value.item[2].value[0].value.item)
def get_accountressources(self):
'''
gets account settings
:return: dict with account data
'''
request = {
'KasUser': self._user,
'KasAuthType': 'session',
'KasAuthData': self._auth_token,
'KasRequestType': "get_accountressources",
'KasRequestParams': "",
}
response = self._send_request(request)
return self._convert_to_dict(response.item[1].value.item[2].value[0])
def get_server_information(self):
'''
gets server information
:return: dict with server information
'''
request = {
'KasUser': self._user,
'KasAuthType': 'session',
'KasAuthData': self._auth_token,
'KasRequestType': "get_server_information",
'KasRequestParams': "",
}
response = self._send_request(request)
out = []
for listelement in response.item[1].value.item[2].value:
out.append(self._convert_to_dict(listelement))
return out
def fix_chown_path(self, path):
'''
remove /www/htdocs/w123456 from path as the KAS API for update_chown
expects it that way
:param path: path to fix
:return: fixed path
'''
path_to_remove = os.path.join("www", "htdocs", self.get_user())
return path.replace(path_to_remove, "")
def update_chown(self, owner, path, recursive=False):
'''
change owner of path to user
:param owner: new owner
:param path: path to change, relative to the ftp root,
see fix_chown_path
:param recursive: change recursive?
:return:
'''
request = {
'KasUser': self._user,
'KasAuthType': 'session',
'KasAuthData': self._auth_token,
'KasRequestType': "update_chown",
'KasRequestParams': {
'chown_path': path,
'chown_user': owner,
'recursive': self._convert_bool_to_str(recursive)
},
}
response = self._send_request(request)
def is_local(self):
'''
checks if we are on a KAS system
:return: True if yes
'''
if socket.getfqdn().endswith(".kasserver.com"):
return True
else:
return False
def get_user(self):
'''
returns KAS username if were are local on KAS system
:return: KAS username
'''
if not self.is_local():
raise IOError("Not on KAS!")
username = getpass.getuser()
username = username.replace("ssh-", "")
return username
def _convert_bool_to_str(self, input):
'''
helper function which converts logical values to "Y" or "N"
:param input: logical value
:return: "Y" if input==True "N" otherwise
'''
if input:
return "Y"
else:
return "N"