# -*- 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.

# modules
from socket import error
import getpass
import pam

# X2Go Session Broker modules
import x2gobroker.authservice
from x2gobroker.loggers import logger_error

class X2GoBrokerAuthMech(object):
    """\

    X2Go Session Broker's **PAM** *authentication mechanism*:

        This is the most commonly used and most flexible authentication
        mechanism in X2Go Session Broker. You can run the full scope of
        PAM authentication mechanisms (POSIX, LDAP, Kerberos, etc.) over
        it.

        **NOTE:** You can fine-tune PAM's authentication backends in the
        corresponding PAM service file ``/etc/pam.d/x2gobroker``.

        **WARNING:** The PAM authentication mechanism requires an extra
        X2Go Session Broker tool: the X2Go Session Broker's
        Authentication Service. Reason: Some PAM authentication
        modules (e.g. ``pam_unix.so``) require root privileges during the
        authentication process. The X2Go Session Broker's Auth Service
        runs with these root privileges and provides a communication socket to
        the X2Go Session Broker where authentication requests are proxied
        over. See :func:`x2gobroker.authservice.authenticate()`.

        If you don't need root privileges for PAM authentication (e.g.
        LDAP), simply don't run the X2Go Broker Auth Service and
        authentication against PAM are done directly by the session
        broker as system user ``x2gobroker``.
    """

    def authenticate(self, username, password, **kwargs):
        """\
        The **PAM** authentication mechanism's :func:`authenticate()`
        tries to proxy authentication through X2Go Session Broker's Auth
        Service first and, if that fails, attempts another authentication
        against PAM directly (which fails for some PAM modules).

        It returns ``True`` to the user, if authentication against PAM
        has been successful.

        :param username: The broker username sent by the client
        :type username: ``str``
        :param password: The broker password sent by the client
        :type password: ``str``
        :param kwargs: Any other parameter (for future features' compatibility, all ignored for now)
        :type kwargs: ``dict``

        :returns: Authentication success or failure.
        :rtype: ``bool``

        """

        if username and password:
            try:
                # query the X2Go Session Broker's PAM Auth Service
                if x2gobroker.authservice.authenticate(username, password, service="x2gobroker"):
                    return True

            except error:
                logger_error.error('pam_authmech.X2GoBrokerAuthmech.authenticate(): Authentication against authentication service failed, trying direct PAM authentication (which is likely to fail on most PAM setups).')
                logger_error.error('pam_authmech.X2GoBrokerAuthmech.authenticate(): Make sure the current user ({user}) is allowed to use the PAM authentication mechanism.'.format(user=getpass.getuser()))
                # fallback to direct PAM authentication against the PAM service ,,x2gobroker''
                opam = pam
                if hasattr(pam, "pam"):
                    opam = pam.pam()
                if opam.authenticate(username, password, service="x2gobroker"):
                    return True

        return False
