Source code for x2gobroker.brokers.inifile_broker
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
# Copyright (C) 2012-2020 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