This is an automated email from the git hooks/post-receive script. x2go pushed a change to branch master in repository x2gobroker. from c6d8a32 Ignore SSH broker events for now. Not sure if we will ever support that. new 65af5aa debian/python-x2gobroker-doc.doc-base: Drop leading white-space in Abstract: field. new 3ec8cda Finalize API documentation. The 2 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Summary of changes: debian/changelog | 3 + debian/python-x2gobroker-doc.doc-base | 2 +- x2gobroker/agent.py | 37 +++++++--- x2gobroker/authmechs/base_authmech.py | 1 - x2gobroker/authservice.py | 8 ++ x2gobroker/basicauth.py | 3 + x2gobroker/brokers/base_broker.py | 69 +++++++++++++++--- x2gobroker/brokers/inifile_broker.py | 70 ++++++++++++++++++ x2gobroker/brokers/zeroconf_broker.py | 33 +++++++++ x2gobroker/client/plain.py | 28 +++++++ x2gobroker/nameservices/base_nameservice.py | 93 ++++++++++++++++++++++++ x2gobroker/nameservices/libnss_nameservice.py | 40 ++++++++++ x2gobroker/nameservices/testsuite_nameservice.py | 45 +++++++++++- x2gobroker/optional_scripts/base_script.py | 37 ++++++++++ x2gobroker/web/extras.py | 7 ++ x2gobroker/web/json.py | 38 ++++++++-- x2gobroker/web/plain.py | 36 +++++++-- x2gobroker/web/uccs.py | 48 ++++++++++++ x2gobroker/x2gobroker_exceptions.py | 21 +++++- 19 files changed, 580 insertions(+), 39 deletions(-) -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 65af5aa2de36eb90665cc1ae82e938e58bf65325 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Jan 31 20:49:29 2019 +0100 debian/python-x2gobroker-doc.doc-base: Drop leading white-space in Abstract: field. --- debian/changelog | 2 ++ debian/python-x2gobroker-doc.doc-base | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 726cd82..04df2b4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -118,6 +118,8 @@ x2gobroker (0.0.4.0-0x2go1) UNRELEASED; urgency=medium * debian/x2gobroker-loadchecker.postinst: + Do chown/chmod on the correct file (not authservice.log, but loadchecker.log). + * debian/python-x2gobroker-doc.doc-base: + + Drop leading white-space in Abstract: field. * x2gobroker.spec: + Adapt to Python3 port. + Bump package version. diff --git a/debian/python-x2gobroker-doc.doc-base b/debian/python-x2gobroker-doc.doc-base index b2e0156..752d53a 100644 --- a/debian/python-x2gobroker-doc.doc-base +++ b/debian/python-x2gobroker-doc.doc-base @@ -2,7 +2,7 @@ Document: python-x2gobroker Title: Python X2Go Broker API Author: Mike Gabriel Abstract: This document describes the internals of the X2Go Session Broker and - how to extend it + how to extend it Section: Programming/Python Format: HTML -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git
This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository x2gobroker. commit 3ec8cdae957979fe8dd994b37da7aa26b2a20a40 Author: Mike Gabriel <mike.gabriel@das-netzwerkteam.de> Date: Thu Jan 31 20:50:08 2019 +0100 Finalize API documentation. --- debian/changelog | 1 + x2gobroker/agent.py | 37 +++++++--- x2gobroker/authmechs/base_authmech.py | 1 - x2gobroker/authservice.py | 8 ++ x2gobroker/basicauth.py | 3 + x2gobroker/brokers/base_broker.py | 69 +++++++++++++++--- x2gobroker/brokers/inifile_broker.py | 70 ++++++++++++++++++ x2gobroker/brokers/zeroconf_broker.py | 33 +++++++++ x2gobroker/client/plain.py | 28 +++++++ x2gobroker/nameservices/base_nameservice.py | 93 ++++++++++++++++++++++++ x2gobroker/nameservices/libnss_nameservice.py | 40 ++++++++++ x2gobroker/nameservices/testsuite_nameservice.py | 45 +++++++++++- x2gobroker/optional_scripts/base_script.py | 37 ++++++++++ x2gobroker/web/extras.py | 7 ++ x2gobroker/web/json.py | 38 ++++++++-- x2gobroker/web/plain.py | 36 +++++++-- x2gobroker/web/uccs.py | 48 ++++++++++++ x2gobroker/x2gobroker_exceptions.py | 21 +++++- 18 files changed, 577 insertions(+), 38 deletions(-) diff --git a/debian/changelog b/debian/changelog index 04df2b4..694f32f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -99,6 +99,7 @@ x2gobroker (0.0.4.0-0x2go1) UNRELEASED; urgency=medium Popen.terminate() (which is unneeded anyway) the process after its execution. - Ignore SSH broker events for now. Not sure if we will ever support that. + - Finalize API documentation. * debian/*: + Trigger Makefile's install target and install those files. Drop debhelper from-source-installation magic. diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py index 22a5527..3647b8b 100644 --- a/x2gobroker/agent.py +++ b/x2gobroker/agent.py @@ -48,6 +48,9 @@ def has_remote_broker_agent_setup(): - Check for available SSH private keys. - Nothing else, so far... + :returns: ``True``, if the broker supports remote broker agent calls + :rtype: ``bool`` + """ home = os.path.expanduser("~") if os.path.exists(os.path.join(home, '.ssh', 'id_rsa')): @@ -76,6 +79,10 @@ def call_broker_agent(username, task, cmdline_args=[], remote_agent=None, logger :raises X2GoBrokerAgentException: if the call to the remote broker agents fails. + :returns: ``(<success>, <data>)``, a tuple with the <success> flag as first item + and the data retrieved from the broker agent as second item + :rtype: ``tuple`` + """ if remote_agent in ('LOCAL', None): result = _call_local_broker_agent(username=username, task=task, cmdline_args=cmdline_args, logger=logger) @@ -99,6 +106,10 @@ def _call_local_broker_agent(username, task, cmdline_args=[], logger=None): :raises X2GoBrokerAgentException: if the call to the remote broker agents fails. + :returns: ``(<success>, <data>)``, a tuple with the <success> flag as first item + and the data retrieved from the broker agent as second item + :rtype: ``tuple`` + """ if logger is None: logger = logger_broker @@ -173,6 +184,10 @@ def _call_remote_broker_agent(username, task, cmdline_args=[], remote_agent=None :raises X2GoBrokerAgentException: if the call to the remote broker agents fails. + :returns: ``(<success>, <data>)``, a tuple with the <success> flag as first item + and the data retrieved from the broker agent as second item + :rtype: ``tuple`` + """ if logger is None: logger = logger_broker @@ -288,7 +303,7 @@ def list_sessions(username, remote_agent=None, logger=None, **kwargs): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, <list-of-sessions>), a tuple with the <success> flag as first item + :returns: ``(<success>, <list-of-sessions>)``, a tuple with the <success> flag as first item and a session ``list`` as second item :rtype: ``tuple`` @@ -311,7 +326,7 @@ def suspend_session(username, session_name, remote_agent=None, logger=None, **kw :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, ), a tuple with the <success> flag as first item + :returns: ``(<success>, [])``, a tuple with the <success> flag as first item :rtype: ``tuple`` """ @@ -333,7 +348,7 @@ def terminate_session(username, session_name, remote_agent=None, logger=None, ** :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, ), a tuple with the <success> flag as first item + :returns: ``(<success>, [])``, a tuple with the <success> flag as first item :rtype: ``tuple`` """ @@ -356,7 +371,7 @@ def has_sessions(username, remote_agent=None, logger=None, **kwargs): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, <has-running-sessions>, <has-suspended-session>), a tuple of two Boolean values + :returns: ``(<success>, <has-running-sessions>, <has-suspended-session>)``, a tuple of two Boolean values :rtype: ``tuple`` """ @@ -385,7 +400,7 @@ def find_busy_servers(username, remote_agent=None, logger=None, **kwargs): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, <server-usage>), a tuple with the <success> flag as first item + :returns: ``(<success>, <server-usage>)``, a tuple with the <success> flag as first item and a dict reflecting the relative server usage :rtype: ``tuple`` @@ -418,7 +433,7 @@ def check_load(remote_agent=None, logger=None, **kwargs): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, <load-factor>), a tuple with the <success> flag as first item + :returns: ``(<success>, <load-factor>)``, a tuple with the <success> flag as first item and the queried server's load factor as second item :rtype: ``tuple`` @@ -475,7 +490,7 @@ def add_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/aut :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, ), a tuple with the <success> flag as first item + :returns: ``(<success>, [])``, a tuple with the <success> flag as first item :rtype: ``tuple`` """ @@ -501,7 +516,7 @@ def delete_authorized_key(username, pubkey_hash, authorized_keys_file='%h/.x2go/ :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, ), a tuple with the <success> flag as first item + :returns: ``(<success>, [])``, a tuple with the <success> flag as first item :rtype: ``tuple`` """ @@ -536,7 +551,7 @@ def get_servers(username, remote_agent=None, logger=None, **kwargs): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, <server-list>), a tuple with the <success> flag as first item + :returns: ``(<success>, <server-list>)``, a tuple with the <success> flag as first item and the list of used X2Go Servers as second item :rtype: ``tuple`` @@ -572,7 +587,7 @@ def tasks_available(username, remote_agent=None, logger=None, **kwargs): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: (<success>, <server-list>), a tuple with the <success> flag as first item + :returns: ``(<success>, <server-list>)``, a tuple with the <success> flag as first item and a list of available broker agent tasks as second item :rtype: ``tuple`` @@ -597,7 +612,7 @@ def genkeypair(local_username, client_address, key_type='RSA', logger=None): :param logger: logger instance to report log messages to :type logger: :class:`logging.<Some>Logger` - :returns: two-item tuple: (<pubkey>, <privkey>) + :returns: two-item tuple: ``(<pubkey>, <privkey>)`` :rtype: ``tuple`` """ diff --git a/x2gobroker/authmechs/base_authmech.py b/x2gobroker/authmechs/base_authmech.py index 2973e23..47d006d 100644 --- a/x2gobroker/authmechs/base_authmech.py +++ b/x2gobroker/authmechs/base_authmech.py @@ -49,6 +49,5 @@ class X2GoBrokerAuthMech(object): :returns: Authentication failure (always!) :rtype: ``bool`` - """ return False diff --git a/x2gobroker/authservice.py b/x2gobroker/authservice.py index 2bac9bc..01cbf95 100644 --- a/x2gobroker/authservice.py +++ b/x2gobroker/authservice.py @@ -135,6 +135,10 @@ class AuthClient(asyncore.dispatcher_with_send): self.logger.info('authentication failure for \'{user}\' with password \'<hidden>\' against PAM service \'{service}\''.format(user=user, service=service)) def handle_close(self): + """\ + Close the connected :class:``AuthClient`` connection. + + """ self.close() @@ -170,5 +174,9 @@ class AuthService(asyncore.dispatcher_with_send): self.listen(1) def handle_accept(self): + """\ + Handle accepted connection requests. + + """ conn, _ = self.accept() AuthClient(conn, logger=self.logger) diff --git a/x2gobroker/basicauth.py b/x2gobroker/basicauth.py index d48117b..f4da6aa 100644 --- a/x2gobroker/basicauth.py +++ b/x2gobroker/basicauth.py @@ -28,6 +28,9 @@ def require_basic_auth(realm, validate_callback): """\ Handler for ``http(s)://`` BasisAuth processing. + This function is used as a decorator for web request handler classes + (such as tornado.web.RequestHandler). + :param realm: authentication realm :type realm: ``str`` :param validate_callback: callback function for validating credentials diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py index 562620c..f70c558 100644 --- a/x2gobroker/brokers/base_broker.py +++ b/x2gobroker/brokers/base_broker.py @@ -1039,7 +1039,8 @@ class X2GoBroker(object): :param profile_id: choose remote agent for this profile ID :type profile_id: ``str`` - :param exclude_agents: a list of remote agent dict objects to be exclude from the random choice + :param exclude_agents: a list of remote agent dict objects to be exclude + from the random choice :type exclude_agents: ``list`` :returns: remote agent to use for queries for profile ID @@ -1280,7 +1281,8 @@ class X2GoBroker(object): def list_profiles(self, username): """\ - Retrieve a list of available session profiles for the authenticated user. + Retrieve a list of available session profiles for the + authenticated user. :param username: query session profile list for this user :type username: ``str`` @@ -1299,12 +1301,14 @@ class X2GoBroker(object): def select_session(self, profile_id, username=None, pubkey=None): """\ - Start/resume a session by selecting a profile name offered by the X2Go client. + Start/resume a session by selecting a profile name offered by the + X2Go client. - The X2Go server that the session is launched on is selected automatically by the X2Go session - broker. + The X2Go server that the session is launched on is selected + automatically by the X2Go session broker. - :param profile_id: the selected profile ID. This matches one of the dictionary keys offered by the ``list_profiles`` method + :param profile_id: the selected profile ID. This matches one of the dictionary + keys offered by the ``list_profiles`` method :type profile_id: ``str`` :param username: specify X2Go Server username that this operation runs for :type username: ``str`` @@ -1312,6 +1316,9 @@ class X2GoBroker(object): temporarily install into a remote X2Go Server for non-interactive login :type pubkey: ``str`` + :returns: the seclected session (X2Go session ID) + :rtype: ``str`` + """ try: profile = self.get_profile(profile_id) @@ -1706,21 +1713,59 @@ class X2GoBroker(object): def change_password(self, new='', old=''): """\ - Modify the authenticated user's password on the X2Go infrastructure (normally, one user - in one X2Go site setup should have the same password on all machines). + Modify the authenticated user's password on the X2Go + infrastructure (normally, one user in one X2Go site setup should + have the same password on all machines). + + This function is a dummy function and needs to be overridden in + specific broker backend implementations + + :param new: the new password that is to be set + :type new: ``str`` + :param old: the currently set password + :type old: ``str`` + + :returns: whether the password change has been successful + :rtype: ``bool`` """ return False def run_optional_script(self, script_type, username, password, task, profile_id, ip, cookie, authed=None, server=None): """\ - Run all optional scripts of type script_type. Called with 3 different script types: + Run all optional scripts of type script_type. Called with 3 + different script types: - pre_auth_scripts - before authentication happens - - post_auth_scripts - after authentication but before anything else occurs - - select_session_scripts - after load balancing before a specific server is sent to the client + - post_auth_scripts - after authentication but before + anything else occurs + - select_session_scripts - after load balancing before a + specific server is sent to the client + + These scripts allow for both addional actions to be performed as + well as the mangling of any relevant fields. - These scripts allow for both addional actions to be performed as well as the mangling of any relevant fields. + :param script_type: name of the script type to be executed (``pre_auth_scripts``, ``post_auth_scripts``, ``select_session_scripts``) + :type script_type: ``str`` + :param username: name of the X2Go session user a script will run for + :type username: ``str`` + :param password: password for the X2Go session + :type password: ``str`` + :param task: the broker task that currently being processed + :type task: ``str`` + :param profile_id: the session profile ID that is being operated upon + :type profile_id: ``str`` + :param ip: the client machine's IP address + :type ip: ``str`` + :param cookie: the currently valid authentication cookie + :type cookie: ``str`` + :param authed: authentication status (already authenticated or not) + :type authed: ``bool`` + :param server: hostname or IP address of the X2Go server being operated upon + :type server: ``str`` + + :returns: Pass-through of the return value returned by the to-be-run optional script (i.e., success or failure) + :rtype: ``bool`` """ diff --git a/x2gobroker/brokers/inifile_broker.py b/x2gobroker/brokers/inifile_broker.py index 72ddd9f..918d0d7 100644 --- a/x2gobroker/brokers/inifile_broker.py +++ b/x2gobroker/brokers/inifile_broker.py @@ -37,11 +37,21 @@ import x2gobroker.x2gobroker_exceptions from configparser import NoSectionError 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. @@ -55,11 +65,32 @@ class X2GoBroker(base.X2GoBroker): self.session_profiles = x2gobroker.config.X2GoBrokerConfigFile(config_files=profile_config_file, defaults=profile_config_defaults) 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() 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-'): @@ -70,7 +101,20 @@ class X2GoBroker(base.X2GoBroker): return profile_defaults 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: @@ -118,7 +162,20 @@ class X2GoBroker(base.X2GoBroker): return profile 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-'): @@ -128,7 +185,20 @@ class X2GoBroker(base.X2GoBroker): return profile 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-'): diff --git a/x2gobroker/brokers/zeroconf_broker.py b/x2gobroker/brokers/zeroconf_broker.py index 656f0dc..697a800 100644 --- a/x2gobroker/brokers/zeroconf_broker.py +++ b/x2gobroker/brokers/zeroconf_broker.py @@ -34,7 +34,20 @@ class X2GoBroker(base.X2GoBroker): backend_name = 'zeroconf' def list_profiles(self, username): + """\ + Retrieve a list of session profiles for the authenticated user. + + With the ``zeroconf`` broker backend, this list of session profiles is + hard-coded. This if for testing purposes, only. + + :param username: query session profile list for this user + :type username: ``str`` + + :returns: list of profile dictionaries + :rtype: ``dict`` + + """ _list_of_profiles = { uuid.uuid4(): { 'user': '', @@ -81,7 +94,27 @@ class X2GoBroker(base.X2GoBroker): return list_of_profiles def select_session(self, profile_id, username=None, **kwargs): + """\ + Start/resume a session by selecting a profile name offered by the + X2Go client. + + With the ``zeroconf`` broker backend, the X2Go server that the + session is launched on is hard-coded (localhost, port 22). This + is for testing purposes only. + + :param profile_id: the selected profile ID. This matches one of the dictionary + keys offered by the ``list_profiles`` method + :type profile_id: ``str`` + :param username: specify X2Go Server username that this operation runs for + :type username: ``str`` + :param pubkey: The broker clients may send us a public key that we may + temporarily install into a remote X2Go Server for non-interactive login + :type pubkey: ``str`` + + :returns: the seclected session (X2Go session ID) + :rtype: ``str`` + """ selectprofile_output = { 'server': 'localhost', 'port': 22, diff --git a/x2gobroker/client/plain.py b/x2gobroker/client/plain.py index fc8ddd9..ec4e8d5 100644 --- a/x2gobroker/client/plain.py +++ b/x2gobroker/client/plain.py @@ -30,8 +30,36 @@ def _override_do_authenticate(username='', password=''): return True class X2GoBrokerClient(object): + """\ + Implementation of a command line interface to X2Go Session Broker. + This CLI can be evoked over an SSH connection. This provides the + so-called SSH mode for X2Go Session Broker. + + """ def get(self, args): + """\ + + Analogy to the http request get method of the HTTP X2Go Session + Broker, this method expects a set of arguments (i.e., an instance + of :class:``argparse.ArgumentParser``) and process the given + arguments. + + Well-known arguments are: + + * ``args.user`` - broker user on whose behalf to operate + * ``args.login`` - X2Go Server user for whom to perform the task + * ``args.auth_cookie`` - authentication cookie + * ``args.task`` - broker backend task to perform + * ``args.profile_id`` - session profile ID + + :param args: an :class:``argparse.ArgumentParser`` object provide by the ``x2gobroker`` command line script + :type args: ``obj`` + + :returns: output as expected by the calling client side + :rtype: ``str`` + + """ backend = args.backend if not backend: diff --git a/x2gobroker/nameservices/base_nameservice.py b/x2gobroker/nameservices/base_nameservice.py index 0dc99a2..2f7920b 100644 --- a/x2gobroker/nameservices/base_nameservice.py +++ b/x2gobroker/nameservices/base_nameservice.py @@ -20,28 +20,121 @@ class X2GoBrokerNameService(object): def has_user(self, username): + """\ + Provide information, if the broker knows a given user (or not). + + :param username: name of the user to check + :type username: ``str`` + + :returns: ``True`` if the user is known to the broker, ``False`` if not + :rtype: ``bool`` + + """ return username in self.get_users() def get_users(self): + """\ + Retrieve list of users known to the broker. + + :returns: list of known user names + :rtype: ``list`` + + """ return [] def get_primary_group(self, username): + """\ + Get the primary group of a given user. If the nameservices + backend in use does not support primary groups, an empty string + will be returned. + + :param username: name of the user to get the primary group for + :type username: ``str`` + + :returns: name of the primary group of the given user + :rtype: ``str`` + + """ return '' def has_group(self, group): + """\ + Provide information, if the broker knows a given group (or not). + + :param group: name of the group to check + :type group: ``str`` + + :returns: ``True`` if the group is known to the broker, ``False`` if not + :rtype: ``bool`` + + """ return group in self.get_groups() def get_groups(self): + """\ + Retrieve list of groups known to the broker. + + :returns: list of known group names + :rtype: ``list`` + + """ return [] def is_group_member(self, username, group, primary_groups=False): + """\ + Check, if a given user is member of a given group. + + Optionally, primary group memberships can be considered (or not). + + :param username: name of the user to check + :type username: ``str`` + :param group: name of the group to check + :type group: ``str`` + :param primary_groups: take primary group membership into consideration + or not + :type primary_groups: ``bool`` + + :returns: ``True`` if the user is member of the given group, ``False`` if not + :rtype: ``bool`` + + """ _members = self.get_group_members(group, primary_groups=primary_groups) return username in _members def get_group_members(self, group, primary_groups=False): + """\ + Retrieve a list of users being members of a given group. + + Optionally, primary group memberships can be considered (or not). + + :param group: name of the group to retrieve members of + :type group: ``str`` + :param primary_groups: take primary group membership into consideration + or not + :type primary_groups: ``bool`` + + :returns: list of users that are members of the given group + :rtype: ``list`` + + """ return [] def get_user_groups(self, username, primary_groups=False): + """\ + Retrieve a list of groups that a given user is member of. + + Optionally, primary group memberships can be considered (or not). + + :param username: name of the user to retrieve groupm memberships of + :type username: ``str`` + :param primary_groups: take primary group membership into consideration + or not + :type primary_groups: ``bool`` + + :returns: list of groups that the given user is member of + :rtype: ``list`` + + """ _groups = [] for _group in self.get_groups(): if self.is_group_member(username=username, group=_group, primary_groups=primary_groups): diff --git a/x2gobroker/nameservices/libnss_nameservice.py b/x2gobroker/nameservices/libnss_nameservice.py index 5db99e9..3c7622a 100644 --- a/x2gobroker/nameservices/libnss_nameservice.py +++ b/x2gobroker/nameservices/libnss_nameservice.py @@ -30,16 +30,56 @@ _grp = grp.getgrall() class X2GoBrokerNameService(base.X2GoBrokerNameService): def get_users(self): + """\ + Retrieve list of users from the POSIX nameservices system. + + :returns: list of known user names + :rtype: ``list`` + + """ return [ p.pw_name for p in _pwd ] def get_primary_group(self, username): + """\ + Get the primary group of a given POSIX user. + + :param username: name of the user to get the primary group for + :type username: ``str`` + + :returns: name of the primary group of the given user + :rtype: ``str`` + + """ prim_gid_number = [ p.pw_gid for p in _pwd if p.pw_name == username ][0] return [ g.gr_name for g in _grp if g.gr_gid == prim_gid_number ][0] def get_groups(self): + """\ + Retrieve list of groups from the POSIX nameservices system. + + :returns: list of known group names + :rtype: ``list`` + + """ return [ g.gr_name for g in _grp ] def get_group_members(self, group, primary_groups=False): + """\ + Retrieve a list of POSIX users being members of a given POSIX + group. + + Optionally, primary group memberships can be considered (or not). + + :param group: name of the group to retrieve members of + :type group: ``str`` + :param primary_groups: take primary group membership into consideration + or not + :type primary_groups: ``bool`` + + :returns: list of users that are members of the given group + :rtype: ``list`` + + """ _members_from_primgroups = [] if primary_groups: for username in self.get_users(): diff --git a/x2gobroker/nameservices/testsuite_nameservice.py b/x2gobroker/nameservices/testsuite_nameservice.py index a6be6ad..70e5d96 100644 --- a/x2gobroker/nameservices/testsuite_nameservice.py +++ b/x2gobroker/nameservices/testsuite_nameservice.py @@ -33,15 +33,59 @@ _groups = { class X2GoBrokerNameService(base.X2GoBrokerNameService): def get_users(self): + """\ + Retrieve hard-coded list of users that we can use for + unit testing. + + :returns: list of known user names + :rtype: ``list`` + + """ return _users def get_primary_group(self, username): + """\ + In POSIX, the primary group name is equal to the user name. + As this is the only straw we can grab during unit tests, we + return the username here. + + :param username: name of the user to get the primary group for + :type username: ``str`` + + :returns: name of the primary group of the given user + :rtype: ``str`` + + """ return username def get_groups(self): + """\ + Retrieve hard-coded list of groups that we can use for unit + testing. + + :returns: list of known group names + :rtype: ``list`` + + """ return list(_groups.keys()) + _users def get_group_members(self, group, primary_groups=False): + """\ + Retrieve a list of users being members of a given group. For unit + testing, the group membership relations have been hard-coded. + + Optionally, primary group memberships can be considered (or not). + + :param group: name of the group to retrieve members of + :type group: ``str`` + :param primary_groups: take primary group membership into consideration + or not + :type primary_groups: ``bool`` + + :returns: list of users that are members of the given group + :rtype: ``list`` + + """ _members = [] if group in list(_groups.keys()): _members.extend(_groups[group]) @@ -50,4 +94,3 @@ class X2GoBrokerNameService(base.X2GoBrokerNameService): if group == self.get_primary_group(username): _members.append(username) return _members - diff --git a/x2gobroker/optional_scripts/base_script.py b/x2gobroker/optional_scripts/base_script.py index 4a64160..92b31e5 100755 --- a/x2gobroker/optional_scripts/base_script.py +++ b/x2gobroker/optional_scripts/base_script.py @@ -19,6 +19,43 @@ # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. class X2GoBrokerOptionalScript(object): + """\ + X2Go Session Broker supports running optional Python code that a site admin + can add to the broker installation files. + + All those optional scripts need to be inherited from this class. + + """ def run_me(self, username, password, task, profile_id, ip, cookie, authed, server): + """\ + Dummy :function:``run_me()`` function. If you deploy your own + optional scripts with X2Go Session Broker, make sure that your + class overrides this function. The broker frontends will try to + execute code presented under this method name at pre-auth, + post-auth and session-selected. + + :param script_type: name of the script type to be executed (``pre_auth_scripts``, ``post_auth_scripts``, ``select_session_scripts``) + :type script_type: ``str`` + :param username: name of the X2Go session user a script will run for + :type username: ``str`` + :param password: password for the X2Go session + :type password: ``str`` + :param task: the broker task that currently being processed + :type task: ``str`` + :param profile_id: the session profile ID that is being operated upon + :type profile_id: ``str`` + :param ip: the client machine's IP address + :type ip: ``str`` + :param cookie: the currently valid authentication cookie + :type cookie: ``str`` + :param authed: authentication status (already authenticated or not) + :type authed: ``bool`` + :param server: hostname or IP address of the X2Go server being operated upon + :type server: ``str`` + + :returns: Pass-through of the return value returned by the to-be-run optional script (i.e., success or failure) + :rtype: ``bool`` + + """ return username, password, task, profile_id, ip, cookie, authed, server diff --git a/x2gobroker/web/extras.py b/x2gobroker/web/extras.py index 4e1e8fd..627ff78 100644 --- a/x2gobroker/web/extras.py +++ b/x2gobroker/web/extras.py @@ -34,7 +34,10 @@ class _RequestHandler(tornado.web.RequestHandler): class X2GoBrokerItWorks(_RequestHandler): + """\ + HTTP request handler that simply replies with an "It works" page. + """ http_header_items = { 'Content-Type': 'text/plain; charset=utf-8', 'Expires': '+1h', @@ -54,7 +57,11 @@ class X2GoBrokerItWorks(_RequestHandler): class X2GoBrokerPubKeyService(_RequestHandler): + """\ + HTTP request handler that provides X2Go Session Broker's SSH public + keys, if any are configured. + """ http_header_items = { 'Content-Type': 'text/plain; charset=utf-8', 'Expires': '+1h', diff --git a/x2gobroker/web/json.py b/x2gobroker/web/json.py index a802866..8e1853b 100644 --- a/x2gobroker/web/json.py +++ b/x2gobroker/web/json.py @@ -37,7 +37,16 @@ class _RequestHandler(tornado.web.RequestHandler): class X2GoBrokerWeb(_RequestHandler): + """\ + HTTP request handler that provides the JSON web frontend of the X2Go + Session Broker. + Currently, Python X2Go and all derived X2Go Client applications use + this web frontend / communication protocol format.. + + :raises tornado.web.HTTPError: on authentication failure a 401 error is raised + + """ http_header_items = { 'Content-Type': 'text/json; charset=utf-8', 'Expires': '+1h', @@ -48,20 +57,38 @@ class X2GoBrokerWeb(_RequestHandler): for http_header_item in list(self.http_header_items.keys()): self.set_header(http_header_item, self.http_header_items[http_header_item]) - def get(self, backend): + def get(self, path): + """\ + Implementation of the JSON based broker communication protocol as + used by Python X2Go (via POST requests). + + In debug mode you can test the broker's functionality using a + normal web browser via GET requests. + + :param path: URL path + :type path: ``str`` + + """ if x2gobroker.defaults.X2GOBROKER_DEBUG: logger_broker.warn('GET http request detected, if unwanted: disable X2GOBROKER_DEBUG') - return self.post(backend) + return self.post(path) raise tornado.web.HTTPError(405) - def post(self, backend): + def post(self, path): + """\ + Implementation of the JSON based broker communication protocol as + used by Python X2Go (via POST requests). + + :param path: URL path + :type path: ``str`` + """ self._gen_http_header() - if not backend: + if not path: backend = x2gobroker.defaults.X2GOBROKER_DEFAULT_BACKEND else: - backend = backend.rstrip('/') + backend = path.rstrip('/') if '/' in backend: backend = backend.split('/')[0] @@ -201,4 +228,3 @@ class X2GoBrokerWeb(_RequestHandler): return raise tornado.web.HTTPError(401) - diff --git a/x2gobroker/web/plain.py b/x2gobroker/web/plain.py index 9b1a17e..6183672 100644 --- a/x2gobroker/web/plain.py +++ b/x2gobroker/web/plain.py @@ -33,7 +33,15 @@ class _RequestHandler(tornado.web.RequestHandler): class X2GoBrokerWeb(_RequestHandler): + """\ + HTTP request handler that provides the plain text web frontend of the + X2Go Session Broker. + Currently, X2Go Client uses this webfrontend / communication protocol format. + + :raises tornado.web.HTTPError: on authentication failure a 401 error is raised + + """ http_header_items = { 'Content-Type': 'text/plain; charset=utf-8', 'Expires': '+1h', @@ -44,20 +52,38 @@ class X2GoBrokerWeb(_RequestHandler): for http_header_item in list(self.http_header_items.keys()): self.set_header(http_header_item, self.http_header_items[http_header_item]) - def get(self, backend): + def get(self, path): + """\ + Implementation of the plain text broker communication protocol as + used by X2Go Client (via POST requests). + + In debug mode you can test the broker's functionality using a + normal web browser via GET requests. + + :param path: URL path + :type path: ``str`` + + """ if x2gobroker.defaults.X2GOBROKER_DEBUG: logger_broker.warn('GET http request detected, if unwanted: disable X2GOBROKER_DEBUG') - return self.post(backend) + return self.post(path) raise tornado.web.HTTPError(405) - def post(self, backend): + def post(self, path): + """\ + Implementation of the plain text broker communication protocol as + used by X2Go Client (via POST requests). + + :param path: URL path + :type path: ``str`` + """ self._gen_http_header() - if not backend: + if not path: backend = x2gobroker.defaults.X2GOBROKER_DEFAULT_BACKEND else: - backend = backend.rstrip('/') + backend = path.rstrip('/') if '/' in backend: backend = backend.split('/')[0] diff --git a/x2gobroker/web/uccs.py b/x2gobroker/web/uccs.py index 5bef36e..c1189f5 100644 --- a/x2gobroker/web/uccs.py +++ b/x2gobroker/web/uccs.py @@ -30,7 +30,18 @@ import x2gobroker.basicauth def credentials_validate(username, password): + """\ + Helper function to validate some given credentials. + :param username: the username + :type username: ``str`` + :param password: the user's password + :type password: ``str`` + + :returns: ``(<username>, <success>)`` tuple + :rtype: ``(str, bool)`` + + """ import x2gobroker.brokers.base_broker # FIXME: with the below hack, the backend broker detection in X2GoBrokerWeb is disabled, only global options # from x2gobroker.conf are available here... @@ -54,19 +65,50 @@ class _RequestHandler(tornado.web.RequestHandler): class X2GoBrokerWeb(_RequestHandler): def get(self, path): + """\ + With the UCCS protocol, a HEAD request is sent to the server's + base URL (sort of as an is-alive ping). + + If X2Go Session Broker runs in debug mode, the processing of + the HEAD request reponse is also made available to GET requests. + This means, you can open the base URL in a normal web browser and + get a reply. + + :param path: URL path + :type path: ``str`` + + """ if x2gobroker.defaults.X2GOBROKER_DEBUG: logger_broker.warn('GET http request detected, if unwanted: disable X2GOBROKER_DEBUG') return self.head(path) raise tornado.web.HTTPError(405) def head(self, path): + """\ + With the UCCS protocol, a HEAD request is sent to the server's + base URL (sort of as an is-alive ping). + + :param path: URL path + :type path: ``str`` + + """ self.write(str(datetime.datetime.utcnow())) return @x2gobroker.basicauth.require_basic_auth('Authentication required', credentials_validate) class X2GoBrokerWebAPI(tornado.web.RequestHandler): + """\ + HTTP request handler that provides the UCCS web frontend of the + X2Go Session Broker. + + Currently, Arctica Greeter with Remote Logon Service uses this + webfrontend / communication protocol format. + :raises tornado.web.HTTPError: on authentication failure a 401 + error is raised; on invalid API versions, a 404 error is raised. + + """ def __init__(self, *args, **kwargs): # latest API version is 5 self.api_version = 5 @@ -85,7 +127,13 @@ class X2GoBrokerWebAPI(tornado.web.RequestHandler): self.set_header(http_header_item, self.http_header_items[http_header_item]) def get(self, *args, **kwargs): + """\ + Implementation of the UCCS broker API versions 4 (final) and 5 (in development). + + :param path: URL path + :type path: ``str`` + """ self._gen_http_header() backend = args[0] diff --git a/x2gobroker/x2gobroker_exceptions.py b/x2gobroker/x2gobroker_exceptions.py index b766089..b6e866e 100644 --- a/x2gobroker/x2gobroker_exceptions.py +++ b/x2gobroker/x2gobroker_exceptions.py @@ -20,9 +20,26 @@ from x2gobroker.loggers import logger_error class X2GoBrokerBaseException(BaseException): + """\ + Base exception for all X2Go Session Broker exceptions." + + """ def __init__(self, *args, **kwargs): BaseException.__init__(self, *args, **kwargs) logger_error.error('exception raised: {exception}("{msg}")'.format(exception=type(self).__name__, msg=str(self))) -class X2GoBrokerAgentException(X2GoBrokerBaseException): pass -class X2GoBrokerProfileException(X2GoBrokerBaseException): pass +class X2GoBrokerAgentException(X2GoBrokerBaseException): + """\ + X2Go Broker Agent exception class. Used for failures during broker + <-> broker agent communications. + + """ + pass + +class X2GoBrokerProfileException(X2GoBrokerBaseException): + """\ + X2Go Broker exception class for session profile problems. Used for + failures when parsing and processing session profiles. + + """ + pass -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2gobroker.git