WhintPy 1.1

https://sourceforge.net/projects/whintpy/

Module whintpy.connection

Class Connection

Description

Manage multiple authentication methods.

This class supports dynamically adding and using different authentication methods. The Connection class is used to manage the authentication methods and to authenticate a user.

Example
 >>> connection = Connection()
 >>> # Enable a method without id
 >>> connection.enable_method("ldap", True, "test.dom")
 >>> # Enable a method with id
 >>> connection.enable_method("ldap", True, "cert", id="example_id_ldap")
 >>> # Authenticate a user using the enabled methods
 >>> connection.connect("ldap", "test", "test")
 >>> # Disable a method
 >>> connection.enable_method("ldap", True, "cert", id="example_id_ldap")
 >>> # Get the list of enabled methods
 >>> connection.get_methods_ids()
 > ['ldap_id', 'certificate_id']
 >>> # Get the authentication method by its name
 >>> connection.get_authentication_method_by_name("ldap")

Constructor

Create a Connection instance.

View Source
def __init__(self):
    """Create a Connection instance."""
    self.__enabled_methods = []
    self.__auth_methods = Authentication()

Public functions

check_string

Raise TypeError if the given parameter is not a string.

Parameters
  • s: (str) A string to be checked.
Raises
  • TypeError: the given parameter is not a string.
View Source
@staticmethod
def check_string(s: str) -> None:
    """Raise TypeError if the given parameter is not a string.

        :param s: (str) A string to be checked.
        :raises: TypeError: the given parameter is not a string.

        """
    if isinstance(s, str) is False:
        raise TypeError('Expected a string. Got {} instead.'.format(type(s)))

check_bool

Raise TypeError if the given parameter is not a bool.

Parameters
  • b: (bool) A boolean to be checked.
Raises
  • TypeError: the given parameter is not a boolean.
View Source
@staticmethod
def check_bool(b: bool) -> None:
    """Raise TypeError if the given parameter is not a bool.

        :param b: (bool) A boolean to be checked.
        :raises: TypeError: the given parameter is not a boolean.

        """
    if isinstance(b, bool) is False:
        raise TypeError('Expected a bool. Got {} instead.'.format(type(b)))

get_methods_names

Get the list of enabled authentication method names.

Returns
  • (list) the list of enabled authentication method names
View Source
def get_methods_names(self) -> list:
    """Get the list of enabled authentication method names.

        :return: (list) the list of enabled authentication method names

        """
    return list(set([m.name() for m in self.__enabled_methods]))

get_methods_ids

Get the list of enabled authentication methods.

Returns
  • (list) the list of enabled authentication method identifiers
View Source
def get_methods_ids(self) -> list:
    """Get the list of enabled authentication methods.

        :return: (list) the list of enabled authentication method identifiers

        """
    return [m.get_method_id() for m in self.__enabled_methods if m.get_method_id() is not None]

get_authentication_method_by_name

Get an authentication method by its name.

Parameters
  • name: (str) the name of the method
Raises
  • KeyError: the method is not found
  • TypeError: the name is not a string
Returns
  • (obj) the authentication class
View Source
def get_authentication_method_by_name(self, name: str) -> object:
    """Get an authentication method by its name.

        :param name: (str) the name of the method
        :raises: KeyError: the method is not found
        :raises: TypeError: the name is not a string
        :return: (obj) the authentication class

        """
    Connection.check_string(name)
    for method in self.__enabled_methods:
        if method.name() == name:
            return method
    raise KeyError(f"Connection.get_authentication_method_by_name: No authentication method found with name '{name}'.")

get_authentication_method_by_id

Get the authentication method by its id.

Parameters
  • auth_id: (str) the id of the method
Raises
  • KeyError: the method is not found
  • TypeError: the given id is not a string
Returns
  • (obj) the authentication class
View Source
def get_authentication_method_by_id(self, auth_id: str) -> object:
    """Get the authentication method by its id.

        :param auth_id: (str) the id of the method
        :raises: KeyError: the method is not found
        :raises: TypeError: the given id is not a string
        :return: (obj) the authentication class

        """
    Connection.check_string(auth_id)
    for method in self.__enabled_methods:
        if method.get_method_id() == auth_id:
            return method
    raise KeyError(f"Connection.get_authentication_method_by_id: No authentication method found with id '{auth_id}'.")

enable_method

Enable or disable an authentication method.

It is allowed to enable as many different authentication methods as possible. However, it is not allowed to enable the same method multiple times, except if the authentication instances have different identifiers.

Parameters
  • name: (str) the name of the method to add or remove
  • value: (bool) if the method is enabled or not
  • args: (list) the arguments of the method
  • kwargs: (dict) the keywords arguments of the method
Raises
  • TypeError: the method is not a string
  • TypeError: the value is not a boolean
  • KeyError: unknown method name
Returns
  • (bool) if enabled or disabled
View Source
def enable_method(self, name: str, value: bool, *args, **kwargs) -> bool:
    """Enable or disable an authentication method.

        It is allowed to enable as many different authentication methods as possible.
        However, it is not allowed to enable the same method multiple times, except
        if the authentication instances have different identifiers.

        :param name: (str) the name of the method to add or remove
        :param value: (bool) if the method is enabled or not
        :param args: (list) the arguments of the method
        :param kwargs: (dict) the keywords arguments of the method
        :raises: TypeError: the method is not a string
        :raises: TypeError: the value is not a boolean
        :raises: KeyError: unknown method name
        :return: (bool) if enabled or disabled

        """
    Connection.check_string(name)
    Connection.check_bool(value)
    method_id = None
    if 'method_id' in kwargs:
        method_id = kwargs['method_id']
        kwargs.pop('method_id')
    if value is True:
        return self.__enable(name, method_id, *args, **kwargs)
    else:
        return self.__disable(name, method_id)

connect

Authenticate a user using the specified method.

Parameters
  • name: (str) the method to use for authentication
  • args: the credentials required by the authentication method
  • kwargs: the id of the method if it is needed
Raises
  • TypeError: the name is not a string
  • TypeError: the args are not a tuple
  • ValueError: the method is not configured
Returns
  • (bool) Authentication success or failure
Example
 >>> connection = Connection()
 >>> connection.enable_method(JwtAuthentication.name(), True, "secret_key", method_id="example_id")
 >>> connection.get_authentication_method_by_id("example_id").generate_token("test")
 >>> connection.connect(JwtAuthentication.name(), "test", method_id="example_id")

TODO: re-write this method to turn it into something humanly understandable, and test it.

View Source
def connect(self, name: str, *args, **kwargs) -> tuple[bool, str]:
    """Authenticate a user using the specified method.

        :param name: (str) the method to use for authentication
        :param args: the credentials required by the authentication method
        :param kwargs: the id of the method if it is needed
        :raises: TypeError: the name is not a string
        :raises: TypeError: the args are not a tuple
        :raises: ValueError: the method is not configured
        :return: (bool) Authentication success or failure

        :example:
        >>> connection = Connection()
        >>> connection.enable_method(JwtAuthentication.name(), True, "secret_key", method_id="example_id")
        >>> connection.get_authentication_method_by_id("example_id").generate_token("test")
        >>> connection.connect(JwtAuthentication.name(), "test", method_id="example_id")

        TODO: re-write this method to turn it into something humanly understandable, and test it.

        """
    method_id = None
    if 'method_id' in kwargs:
        method_id = kwargs['method_id']
    Connection.check_string(name)
    if isinstance(args, tuple) is False:
        raise TypeError(f"Connection.connect: Expected a tuple for 'credentials', but received '{type(args).__name__}'.")
    for credential in args:
        if isinstance(credential, str) is False:
            raise TypeError(f"Connection.connect: Expected a tuple of string for 'credentials', but received a '{type(credential).__name__}'. in the tuple.")
    for auth in self.__enabled_methods:
        if auth.name() == name and (method_id is None or auth.get_method_id() == method_id):
            return auth.authenticate(*args)
    raise ValueError(f"Connection.connect: Cannot authenticate using '{name}'. The method is not available or not configured.")

Protected functions

__enable

Enable an authentication method.

Parameters
  • name: (str) the name of the method to add
  • method_id: (str | None) Method identifier
  • args: (dict) the arguments of the credentials depends on the method
  • kwargs: (dict) the arguments of the credentials depends on the method
Raises
  • TypeError: the method is not a string
  • TypeError: the value is not a boolean
  • KeyError: unknown method name
Returns
  • (bool) whether the authentication method has been enabled or not
View Source
def __enable(self, name: str, method_id: str | None, *args, **kwargs) -> bool:
    """Enable an authentication method.

        :param name: (str) the name of the method to add
        :param method_id: (str | None) Method identifier
        :param args: (dict) the arguments of the credentials depends on the method
        :param kwargs: (dict) the arguments of the credentials depends on the method
        :raises: TypeError: the method is not a string
        :raises: TypeError: the value is not a boolean
        :raises: KeyError: unknown method name
        :return: (bool) whether the authentication method has been enabled or not

        """
    if method_id is not None:
        for m in self.__enabled_methods:
            if m.get_method_id() == method_id:
                logging.info(f"Authentication method with identifier '{method_id}' is already enabled.")
                return False
    else:
        for m in self.__enabled_methods:
            if m.name() == name and m.get_method_id() is None:
                logging.info(f"Authentication method with name '{name}' is already enabled. ")
                return False
    auth = self.__auth_methods.get_auth_class(name)(*args, method_id=method_id, **kwargs)
    if auth.available is True:
        self.__enabled_methods.append(auth)
    return auth.available

__disable

Disable an authentication method from its name or identifier.

Parameters
  • name: (str) the name of the method to remove
  • method_id: (str | None) Method identifier
Raises
  • TypeError: the method is not a string
  • TypeError: the value is not a boolean
  • KeyError: unknown method name
Returns
  • (bool) whether the authentication method has been enabled or not
View Source
def __disable(self, name: str, method_id: str | None) -> bool:
    """Disable an authentication method from its name or identifier.

        :param name: (str) the name of the method to remove
        :param method_id: (str | None) Method identifier
        :raises: TypeError: the method is not a string
        :raises: TypeError: the value is not a boolean
        :raises: KeyError: unknown method name
        :return: (bool) whether the authentication method has been enabled or not

        """
    for auth in self.__enabled_methods:
        if auth.name() == name:
            if method_id is None or (method_id is not None and auth.get_method_id() == method_id):
                self.__enabled_methods.remove(auth)
                return True
    logging.info(f"None of the enabled authentication methods are matching both name '{name}' and id '{method_id}'.")
    return False

Overloads

__str__

Return the informal string representation of the class.

Returns
  • (str) The informal string representation
View Source
def __str__(self):
    """Return the informal string representation of the class.

        :return: (str) The informal string representation

        """
    return f'Connection({self.__enabled_methods})'

__repr__

Return the official string representation of the class.

Returns
  • (str) The official string representation
View Source
def __repr__(self):
    """Return the official string representation of the class.

        :return: (str) The official string representation

        """
    enabled_methods_repr = ', '.join(["(name: '{}', id: '{}', args: {})".format(method.name(), method.get_method_id(), method.get_args()) for method in self.__enabled_methods])
    return 'Connection({})'.format(enabled_methods_repr)