Package x2go :: Package backends :: Package profiles :: Module httpbroker
[frames] | no frames]

Source Code for Module x2go.backends.profiles.httpbroker

  1  # -*- coding: utf-8 -*- 
  2   
  3  # Copyright (C) 2010-2016 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de> 
  4  # 
  5  # Python X2Go is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU Affero General Public License as published by 
  7  # the Free Software Foundation; either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Python X2Go is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU Affero General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU Affero General Public License 
 16  # along with this program; if not, write to the 
 17  # Free Software Foundation, Inc., 
 18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 
 19   
 20  """\ 
 21  L{X2GoSessionProfiles} class - managing x2goclient session profiles. 
 22   
 23  L{X2GoSessionProfiles} is a public API class. Use this class in your Python X2Go based 
 24  applications. 
 25   
 26  """ 
 27  __NAME__ = 'x2gosessionprofiles-pylib' 
 28   
 29  import re 
 30  import requests 
 31  import urllib3.exceptions 
 32  import copy 
 33  import types 
 34  import time 
 35  try: import simplejson as json 
 36  except ImportError: import json 
 37   
 38  # Python X2Go modules 
 39  from x2go.defaults import X2GO_SESSIONPROFILE_DEFAULTS as _X2GO_SESSIONPROFILE_DEFAULTS 
 40  from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER 
 41  import x2go.backends.profiles.base as base 
 42  import x2go.log as log 
 43  from x2go.utils import genkeypair 
 44  import x2go.x2go_exceptions 
 45   
46 -class X2GoSessionProfiles(base.X2GoSessionProfiles):
47 48 defaultSessionProfile = copy.deepcopy(_X2GO_SESSIONPROFILE_DEFAULTS) 49
50 - def __init__(self, session_profile_defaults=None, 51 broker_url="http://localhost:8080/json/", 52 broker_username=None, 53 broker_password=None, 54 logger=None, loglevel=log.loglevel_DEFAULT, 55 **kwargs):
56 """\ 57 Retrieve X2Go session profiles from a HTTP(S) session broker. 58 59 @param session_profile_defaults: a default session profile 60 @type session_profile_defaults: C{dict} 61 @param broker_url: URL for accessing the X2Go Session Broker 62 @type broker_url: C{str} 63 @param broker_password: use this password for authentication against the X2Go Session Broker (avoid 64 password string in the C{broker_URL} parameter is highly recommended) 65 @type broker_password: C{str} 66 @param logger: you can pass an L{X2GoLogger} object to the 67 L{x2go.backends.profiles.httpbroker.X2GoSessionProfiles} constructor 68 @type logger: L{X2GoLogger} instance 69 @param loglevel: if no L{X2GoLogger} object has been supplied a new one will be 70 constructed with the given loglevel 71 @type loglevel: C{int} 72 73 """ 74 if broker_url.upper() != "HTTP": 75 match = re.match('^(?P<protocol>(http(|s)))://(|(?P<user>[a-zA-Z0-9_\.-]+)(|:(?P<password>.*))@)(?P<hostname>[a-zA-Z0-9\.-]+)(|:(?P<port>[0-9]+))($|/(?P<path>.*)$)', broker_url) 76 p = match.groupdict() 77 if p['user']: 78 self.broker_username = p['user'] 79 else: 80 self.broker_username = broker_username 81 if p['password']: 82 self.broker_password = p['password'] 83 elif broker_password: 84 self.broker_password = broker_password 85 else: 86 self.broker_password = None 87 88 # fine-tune the URL 89 p['path'] = "/{path}".format(**p) 90 if p['port'] is not None: 91 p['port'] = ":{port}".format(**p) 92 else: 93 p['port'] = '' 94 95 self.broker_url = "{protocol}://{hostname}{port}{path}".format(**p) 96 97 else: 98 self.broker_username = broker_username 99 self.broker_password = broker_password 100 self.broker_url = broker_url 101 102 self.broker_noauth = False 103 self.broker_authid = None 104 self._broker_profile_cache = {} 105 self._mutable_profile_ids = None 106 self._broker_auth_successful = None 107 108 self._broker_type = "http" 109 110 base.X2GoSessionProfiles.__init__(self, session_profile_defaults=session_profile_defaults, logger=logger, loglevel=loglevel) 111 if self.broker_url != "HTTP": 112 self.logger("Using session broker at URL: %s" % self.broker_url, log.loglevel_NOTICE) 113 114 # for broker based autologin, we have to be able to provide public/private key pair 115 self.broker_my_pubkey, self.broker_my_privkey = genkeypair(local_username=_CURRENT_LOCAL_USER, client_address='127.0.0.1')
116
117 - def get_broker_noauth(self):
118 """\ 119 Accessor for the class's C{broker_noauth} property. 120 121 @return: C{True} if the broker probably does not expect authentication. 122 @rtype: C{bool} 123 124 """ 125 return self.broker_noauth
126
127 - def get_broker_username(self):
128 """\ 129 Accessor for the class's C{broker_username} property. 130 131 @return: the username used for authentication against the session broker URL 132 @rtype: C{str} 133 134 """ 135 return self.broker_username
136
137 - def get_broker_url(self):
138 """\ 139 Accessor for the class's C{broker_url} property. 140 141 @return: the session broker URL that was used at broker session instantiation 142 @rtype: C{str} 143 144 """ 145 return self.broker_url
146
147 - def set_broker_url(self, broker_url):
148 """\ 149 Mutator for the class's C{broker_url} property. 150 151 @param broker_url: A new broker URL to use with this instance. Format is 152 C{<protocol>://<hostname>:<port>/<path>} (where protocol has to be C{http} 153 or C{https}. 154 @type broker_url: C{str} 155 156 @return: the session broker URL that was used at broker session instantiation 157 @rtype: C{str} 158 159 """ 160 self.broker_url = broker_url
161
162 - def get_broker_type(self):
163 """\ 164 Accessor of the class's {_broker_type} property. 165 166 @return: either C{http} or C{https}. 167 @rtype: C{str} 168 169 """ 170 return self._broker_type
171
172 - def broker_simpleauth(self, broker_username, broker_password):
173 """\ 174 Attempt a username / password authentication against the instance's 175 broker URL. 176 177 @param broker_username: username to use for authentication 178 @type broker_username: C{str} 179 @param broker_password: password to use for authentication 180 @type broker_password: C{str} 181 182 @return: C{True} if authentication has been successful 183 @rtype: C{bool} 184 185 @raise X2GoBrokerConnectionException: Raised on any kind of connection / 186 authentication failure. 187 188 """ 189 if self.broker_url is not None: 190 request_data = { 191 'user': broker_username or '', 192 } 193 if self.broker_authid is not None: 194 request_data['authid'] = self.broker_authid 195 self.logger("Sending request to broker: user: {user}, authid: {authid}".format(**request_data), log.loglevel_DEBUG) 196 else: 197 if broker_password: 198 request_data['password'] = "<hidden>" 199 else: 200 request_data['password'] = "<EMPTY>" 201 self.logger("Sending request to broker: user: {user}, password: {password}".format(**request_data), log.loglevel_DEBUG) 202 request_data['password'] = broker_password or '' 203 try: 204 r = requests.post(self.broker_url, data=request_data) 205 except (requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, urllib3.exceptions.LocationParseError): 206 raise x2go.x2go_exceptions.X2GoBrokerConnectionException('Failed to connect to URL %s' % self.broker_url) 207 if r.status_code == 200: 208 payload = json.loads(r.text) 209 if not self.broker_authid and not self.broker_password: 210 self.broker_noauth = True 211 elif payload.has_key('next-authid'): 212 self.broker_authid = payload['next-authid'] 213 self.broker_username = broker_username or '' 214 self.broker_password = broker_password or '' 215 self._broker_auth_successful = True 216 self.populate_session_profiles() 217 return True 218 self._broker_auth_successful = False 219 self.broker_authid = None 220 return False
221
222 - def broker_disconnect(self):
223 """\ 224 Disconnect from an (already) authenticated broker session. 225 226 All authentication parameters will be dropped (forgotten) and 227 this instance has to re-authenticate against / re-connect to the 228 session broker before any new interaction with the broker is possible. 229 230 """ 231 _profile_ids = copy.deepcopy(self.profile_ids) 232 233 # forget nearly everything... 234 for profile_id in _profile_ids: 235 self.init_profile_cache(profile_id) 236 try: del self._profile_metatypes[profile_id] 237 except KeyError: pass 238 try: self._profiles_need_profile_id_renewal.remove(profile_id) 239 except ValueError: pass 240 try: del self._cached_profile_ids[profile_id] 241 except KeyError: pass 242 del self.session_profiles[profile_id] 243 self._mutable_profile_ids = None 244 self._broker_auth_successful = False 245 self.broker_authid = None 246 self.broker_password = None 247 self.broker_noauth = False
248
249 - def is_broker_authenticated(self):
250 """\ 251 Detect if an authenticated broker session has already been 252 initiated. Todo so, a simple re-authentication (username, password) 253 will be attempted. If that fails, user credentials are not provided / 254 valid. 255 256 @return: C{True} if the broker session has already been authenticated 257 and user credentials are known / valid 258 @rtype: C{bool} 259 260 """ 261 if self._broker_auth_successful is None: 262 # do a test auth against the given broker URL 263 try: 264 self.broker_simpleauth(self.broker_username, self.broker_password) 265 except x2go.x2go_exceptions.X2GoBrokerConnectionException: 266 self._broker_auth_successful = False 267 return self._broker_auth_successful
268
269 - def broker_listprofiles(self):
270 """\ 271 Obtain a session profile list from the X2Go Session Broker. 272 273 @return: session profiles as a Python dictionary. 274 @rtype: C{dict} 275 276 """ 277 if self.broker_url is not None: 278 request_data = { 279 'task': 'listprofiles', 280 'user': self.broker_username, 281 } 282 if self.broker_authid is not None: 283 request_data['authid'] = self.broker_authid 284 self.logger("Sending request to broker: user: {user}, authid: {authid}, task: {task}".format(**request_data), log.loglevel_DEBUG) 285 else: 286 if self.broker_password: 287 request_data['password'] = "<hidden>" 288 else: 289 request_data['password'] = "<EMPTY>" 290 self.logger("Sending request to broker: user: {user}, password: {password}, task: {task}".format(**request_data), log.loglevel_DEBUG) 291 request_data['password'] = self.broker_password or '' 292 try: 293 r = requests.post(self.broker_url, data=request_data) 294 except requests.exceptions.ConnectionError: 295 raise x2go.x2go_exceptions.X2GoBrokerConnectionException('Failed to connect to URL %s' % self.broker_url) 296 if r.status_code == 200 and r.headers['content-type'].startswith("text/json"): 297 payload = json.loads(r.text) 298 if payload.has_key('next-authid'): 299 self.broker_authid = payload['next-authid'] 300 if payload.has_key('mutable_profile_ids'): 301 self._mutable_profile_ids = payload['mutable_profile_ids'] 302 self._broker_auth_successful = True 303 return payload['profiles'] if payload['task'] == 'listprofiles' else {} 304 self._broker_auth_successful = False 305 self.broker_authid = None 306 return {}
307
308 - def broker_selectsession(self, profile_id):
309 """\ 310 Select a session from the list of available session profiles (presented by 311 L{broker_listprofiles}). This method requests a session information dictionary 312 (server, port, SSH keys, already running / suspended sessions, etc.) from the 313 session broker for the provided C{profile_id}. 314 315 @param profile_id: profile ID of the selected session profile 316 @type profile_id: C{str} 317 318 @return: session information (server, port, SSH keys, etc.) for a selected 319 session profile (i.e. C{profile_id}) 320 @rtype: C{dict} 321 322 """ 323 if self.broker_url is not None: 324 if not self._broker_profile_cache.has_key(profile_id) or not self._broker_profile_cache[profile_id]: 325 request_data = { 326 'task': 'selectsession', 327 'profile-id': profile_id, 328 'user': self.broker_username, 329 'pubkey': self.broker_my_pubkey, 330 } 331 if self.broker_authid is not None: 332 request_data['authid'] = self.broker_authid 333 self.logger("Sending request to broker: user: {user}, authid: {authid}, task: {task}".format(**request_data), log.loglevel_DEBUG) 334 else: 335 if self.broker_password: 336 request_data['password'] = "<hidden>" 337 else: 338 request_data['password'] = "<EMPTY>" 339 self.logger("Sending request to broker: user: {user}, password: {password}, task: {task}".format(**request_data), log.loglevel_DEBUG) 340 request_data['password'] = self.broker_password or '' 341 try: 342 r = requests.post(self.broker_url, data=request_data) 343 except requests.exceptions.ConnectionError: 344 raise x2go.x2go_exceptions.X2GoBrokerConnectionException('Failed to connect to URL %s' % self.broker_url) 345 if r.status_code == 200 and r.headers['content-type'].startswith("text/json"): 346 payload = json.loads(r.text) 347 if payload.has_key('next-authid'): 348 self.broker_authid = payload['next-authid'] 349 self._broker_profile_cache[profile_id] = payload['selected_session'] if payload['task'] == 'selectsession' else {} 350 self._broker_auth_successful = True 351 else: 352 self.broker_authid = None 353 self._broker_auth_successful = False 354 self._broker_profile_cache[profile_id] 355 return self._broker_profile_cache[profile_id] 356 return {}
357
358 - def _init_profile_cache(self, profile_id):
359 if self._broker_profile_cache.has_key(unicode(profile_id)): 360 del self._broker_profile_cache[unicode(profile_id)]
361
363 """\ 364 Populate the set of session profiles by loading the session 365 profile configuration from a file in INI format. 366 367 @return: a set of session profiles 368 @rtype: C{dict} 369 370 """ 371 if self.is_broker_authenticated() and \ 372 self.broker_noauth or \ 373 self.broker_username and self.broker_password: 374 375 session_profiles = self.broker_listprofiles() 376 _session_profiles = copy.deepcopy(session_profiles) 377 378 for session_profile in _session_profiles: 379 session_profile = unicode(session_profile) 380 for key, default_value in self.defaultSessionProfile.iteritems(): 381 key = unicode(key) 382 if type(default_value) is types.StringType: 383 default_value = unicode(default_value) 384 if not session_profiles[session_profile].has_key(key): 385 session_profiles[session_profile][key] = default_value 386 387 else: 388 session_profiles = {} 389 390 return session_profiles
391
392 - def _is_mutable(self, profile_id):
393 if type(self._mutable_profile_ids) is types.ListType and profile_id in self._mutable_profile_ids: 394 return True 395 return False
396
398 if type(self._mutable_profile_ids) is types.ListType: 399 return True 400 return False
401
402 - def _write(self):
403 print "not suported, yet"
404
405 - def _delete_profile(self, profile_id):
406 del self.session_profiles[unicode(profile_id)]
407
408 - def _update_value(self, profile_id, option, value):
409 if type(value) is types.StringType: 410 value = unicode(value) 411 self.session_profiles[unicode(profile_id)][unicode(option)] = value
412
413 - def _get_profile_parameter(self, profile_id, option, key_type):
414 return key_type(self.session_profiles[unicode(profile_id)][unicode(option)])
415
416 - def _get_profile_options(self, profile_id):
417 return self.session_profiles[unicode(profile_id)].keys()
418
419 - def _get_profile_ids(self):
420 self.session_profiles.keys() 421 return self.session_profiles.keys()
422
423 - def _get_server_hostname(self, profile_id):
424 selected_session = self.broker_selectsession(profile_id) 425 return selected_session['server']
426
427 - def _get_server_port(self, profile_id):
428 selected_session = self.broker_selectsession(profile_id) 429 return int(selected_session['port'])
430
431 - def _get_pkey_object(self, profile_id):
432 selected_session = self.broker_selectsession(profile_id) 433 if selected_session.has_key('authentication_pubkey') and selected_session['authentication_pubkey'] == 'ACCEPTED': 434 time.sleep(2) 435 return self.broker_my_privkey 436 return None
437