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

# Copyright (C) 2012-2020 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
# Copyright (C) 2012-2020 by Josh Lukens <jlukens@botch.com>
#
# 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.

# Very simple authmech that requests a webpage over https with basic auth.
# If the page is fetched successfully (status 200) the user is authenticated.
#
# Used in conjunction with something like an apache server you can get easy
# access to the full handful of existing auth modules for things like radius,
# RSA, etc.
#
# Server name and path must be hard coded below for the time being.  Also note
# that the httplib module used does not verify SSL certificates so be sure
# you are on a trusted network as there is a possibility of a man in the middle
# attack.

# modules
import http.client
import base64

class X2GoBrokerAuthMech(object):
    """\

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

        This authentication mechanism can be attached to a web server
        that provides some test URL protected by http(s) Basic
        Authentication.

        When the :func:`authenticate()` function gets called, it attempts
        to retrieve the test URL via a http(s) GET request. The webserver
        serving that URL then sends a response back, demanding
        ``Authorization``.

        For the Basic Authorization request that gets sent back to the
        webserver, the username and password provided by the X2Go client
        application get used.

    """

    def authenticate(self, username, password, config=None, **kwargs):
        """\
        The **https_get** authentication mechanism's :func:`authenticate()`
        method attempts authentication against a http(s) server.

        It lets broker authentication succeed if the upstream webserver
        grants authentication to a given test URL. Otherwise, broker
        authencation fails.

        The test URL is provided as set of config parameters passed in
        via the ``config`` function parameter. If no config is given, the
        default authentication will be performed against
        ``http://localhost/auth``.

        The configuration object provided as ``config`` to this method
        requires to understand this API (a class from module
        :mod:`configparser` should do this for you)::

          host = config.get_value('authmech_https_get','host')
          path = config.get_value('authmech_https_get','path')
          port = config.get_value('authmech_https_get','port')

        :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 config: A :mod:`configparser` compliant configuration object
        :param type: ``<configparser-like-obj>``
        :param kwargs: Any other parameter (for future features' compatibility, all ignored for now)
        :type kwargs: ``dict``

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

        """

        ## FIXME: these should really be specificed in master config file and have better error checking

        if config:
            host = config.get_value('authmech_https_get','host')
            path = config.get_value('authmech_https_get','path')
            port = config.get_value('authmech_https_get','port')
        else:
            host = "localhost"
            path = "/auth"
            port = "80"

        # base64 encode the username and password
        auth = base64.standard_b64encode('%s:%s' % (username, password)).replace('\n', '')

        https = http.client.HTTPSConnection(host,port)
        https.putrequest("GET", path)
        https.putheader("Host", host)
        https.putheader("User-Agent", "X2Go Session Broker")
        https.putheader("Authorization", "Basic %s" % auth)
        https.endheaders()

        response = https.getresponse()
        https.close()

        if response.status == 200:
            return True

        return False
