Source code for x2gobroker.brokers.inifile_broker

# -*- coding: utf-8 -*-
# vim:fenc=utf-8

# Copyright (C) 2012-2019 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
#
# X2Go Session Broker is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# X2Go Session Broker is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

"""\
:class:`x2gobroker.brokers.inifile_broker.X2GoBroker` class - a simple X2GoBroker implementations that uses text-based config files (also supports load balancing)

"""
__NAME__ = 'x2gobroker-pylib'

# modules
import copy
import netaddr
import re

# Python X2GoBroker modules
import x2gobroker.brokers.base_broker as base
import x2gobroker.config
import x2gobroker.defaults
import x2gobroker.x2gobroker_exceptions

from configparser import NoSectionError

[docs]class X2GoBroker(base.X2GoBroker): """\ :class:`x2gobroker.brokers.inifile_broker.X2GoBroker` implements a broker backend retrieving its session profile and ACL configuration from a file in INI file format. """ backend_name = 'inifile' def __init__(self, profile_config_file=None, profile_config_defaults=None, **kwargs): """\ Initialize a new INI file based X2GoBroker instance to control X2Go session through an X2Go Client with an intermediate session broker :param profile_config_file: path to the backend's session profile configuration (x2gobroker-sessionprofiles.conf) :type profile_config_file: ``str`` :param profile_config_defaults: Default settings for session profile configuration parameters. :type profile_config_defaults: ``dict`` """ base.X2GoBroker.__init__(self, **kwargs) if profile_config_file is None: profile_config_file = x2gobroker.defaults.X2GOBROKER_SESSIONPROFILES if profile_config_defaults is None: profile_config_defaults = x2gobroker.defaults.X2GOBROKER_SESSIONPROFILE_DEFAULTS self.session_profiles = x2gobroker.config.X2GoBrokerConfigFile(config_files=profile_config_file, defaults=profile_config_defaults)
[docs] def get_profile_ids(self): """\ Retrieve the complete list of session profile IDs. With the ``inifile`` broker backend, the profile IDs are the names of the INI file's sections. :returns: list of profile IDs :rtype: ``list`` """ return self.session_profiles.list_sections()
[docs] def get_profile_defaults(self): """\ Get the session profile defaults, i.e. profile options that all configured session profiles have in common. The defaults are hard-coded in :mod:`x2gobroker.defaults` for class :class:`x2gobroker.brokers.base_broker.X2GoBroker`. With the ``inifile`` backend, they can be overridden/customized under the INI file's ``[DEFAULT]`` section. :returns: a dictionary containing the session profile defaults :rtype: ``dict`` """ profile_defaults = self.session_profiles.get_defaults() for key in list(profile_defaults.keys()): if key.startswith('acl-'): del profile_defaults[key] elif type(profile_defaults[key]) == list: profile_defaults.update({ key: [ v for v in profile_defaults[key] if v ] }) return profile_defaults
[docs] def get_profile(self, profile_id): """\ Get the session profile for profile ID <profile_id>. With the ``inifile`` broker backend, the session profile parameters are the given ``<parameter>=<value>`` pairs under the section ``[<profile_id>]``. :param profile_id: the ID of a profile :type profile_id: ``str`` :returns: a dictionary representing the session profile for ID <profile_id> :rtype: ``dict`` """ try: profile = self.session_profiles.get_section(profile_id) except NoSectionError: raise x2gobroker.x2gobroker_exceptions.X2GoBrokerProfileException('No such session profile ID: {profile_id}'.format(profile_id=profile_id)) profile_defaults = self.get_profile_defaults() for key in list(profile_defaults.keys()): if key not in list(profile.keys()): profile.update({ key: profile_defaults[key] }) if type(profile_defaults[key]) == list: profile.update({ key: [ v for v in profile[key] if v ] }) for key in list(profile.keys()): if key.startswith('acl-'): del profile[key] if key.startswith('broker-'): del profile[key] if key == 'default': del profile[key] if key == 'host': _hosts = copy.deepcopy(profile[key]) try: _default_sshport = int(profile['sshport']) except TypeError: _default_sshport = 22 profile[key] = [] for host in _hosts: if re.match('^.*\ \(.*\)$', host): _hostname = host.split(' ')[0] _address = host.split(' ')[1][1:-1] _address, _port = x2gobroker.utils.split_host_address(_address, default_port=_default_sshport) # test if _address is a valid hostname, a valid DNS name or an IPv4/IPv6 address if (re.match('(?!-)[A-Z\d-]{1,63}(?<!-)$', _hostname, flags=re.IGNORECASE) or \ re.match('(?=^.{1,254}$)(^(?:(?!\d|-)[a-zA-Z0-9\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)', _hostname, flags=re.IGNORECASE)) and \ (re.match('(?=^.{1,254}$)(^(?:(?!\d|-)[a-zA-Z0-9\-]{1,63}(?<!-)\.?)+(?:[a-zA-Z]{2,})$)', _address, flags=re.IGNORECASE) or \ netaddr.valid_ipv4(_address) or netaddr.valid_ipv6(_address)): profile["host={hostname}".format(hostname=_hostname)] = _address if _port != _default_sshport: profile["sshport={hostname}".format(hostname=_hostname)] = _port profile[key].append(_hostname) else: profile[key].append(host) return profile
[docs] def get_profile_broker(self, profile_id): """\ Get broker-specific session profile options from the session profile with profile ID <profile_id>. With the ``inifile`` broker backend, these broker specific options are ``<param>=<value>`` pairs prefixed like this: ``broker-<param>=<value>`` :param profile_id: the ID of a profile :type profile_id: ``str`` :returns: a dictionary representing the session profile for ID <profile_id> :rtype: ``dict`` """ profile = self.session_profiles.get_section(profile_id) for key in list(profile.keys()): if not key.startswith('broker-'): del profile[key] if key.startswith('broker-') and (profile[key] == '' or profile[key] == ['']): del profile[key] return profile
[docs] def get_profile_acls(self, profile_id): """\ Get the ACLs for session profile with profile ID <profile_id>. With the ``inifile`` broker backend, these ACL specific options are ``<param>=<value>`` pairs prefixed like this: ``acl-<param>=<value>`` :param profile_id: the ID of a profile :type profile_id: ``str`` :returns: a dictionary representing the ACLs for session profile with ID <profile_id> :rtype: ``dict`` """ profile = self.session_profiles.get_section(profile_id) for key in list(profile.keys()): if not key.startswith('acl-'): del profile[key] if key.startswith('acl-') and (profile[key] == '' or profile[key] == ['']): del profile[key] return profile