Source code for requests_gracedb.auth

#
# Copyright (C) 2019-2025  Leo P. Singer <leo.singer@ligo.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
from os import R_OK, access
from urllib.parse import urlparse

from igwn_auth_utils.scitokens import default_bearer_token_file
from safe_netrc import netrc

from .scitoken import SciTokenAuth, SciTokenReloadingAuth


def _find_username_password(url):
    host = urlparse(url).hostname

    try:
        result = netrc().authenticators(host)
    except IOError:
        result = None

    if result is not None:
        username, _, password = result
        result = (username, password)

    return result


def _find_token():
    path = default_bearer_token_file()
    if access(path, R_OK):
        return path


[docs] class SessionAuthMixin: """A mixin for :class:`requests.Session` to add support for all GraceDB authentication mechanisms. Parameters ---------- url : str GraceDB Client URL. token : str Filename for SciTokens bearer token. username : str Username for basic auth. password : str Password for basic auth. force_noauth : bool, default=False If true, then do not use any authentication at all. fail_if_noauth : bool, default=False If true, then raise an exception if authentication credentials are not provided. auth_reload : bool, default=False If true, then automatically reload the authentication before it expires. auth_reload_timeout : int, default=300 Reload the authentication this many seconds before it expires. Notes ----- When a new Session instance is created, the following sources of authentication are tried, in order: 1. If the :obj:`force_noauth` keyword argument is true, then perform no authentication at all. 2. If the :obj:`token` keyword argument is provided, then use SciTokens bearer token authentication. 3. If the :obj:`username` and :obj:`password` keyword arguments are provided, then use basic auth. 4. Look for a SciTokens bearer token in: a. the environment variable :envvar:`BEARER_TOKEN_FILE` b. the file :file:`$XDG_RUNTIME_DIR/bt_u{UID}`, where :samp:`{UID}` is your numeric user ID, if the file exists and is readable 5. Read the netrc file [1]_ located at :file:`~/.netrc`, or at the path stored in the environment variable :envvar:`NETRC`, and look for a username and password matching the hostname in the URL. 6. If the :obj:`fail_if_noauth` keyword argument is true, and no authentication source was found, then raise a :class:`ValueError`. References ---------- .. [1] The .netrc file. https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html """ # noqa: E501 def __init__( self, url=None, token=None, username=None, password=None, force_noauth=False, fail_if_noauth=False, auth_reload=False, auth_reload_timeout=300, **kwargs, ): super(SessionAuthMixin, self).__init__(**kwargs) # Argument validation if fail_if_noauth and force_noauth: raise ValueError("Must not set both force_noauth and fail_if_noauth.") if (username is None) ^ (password is None): raise ValueError("Must provide username and password, or neither.") if force_noauth: pass elif token is not None: pass elif username is not None: self.auth = (username, password) elif (token := _find_token()) is not None: pass elif (default_basic_auth := _find_username_password(url)) is not None: self.auth = default_basic_auth elif fail_if_noauth: raise ValueError("No authentication credentials found.") if token is not None: if auth_reload: self.auth = SciTokenReloadingAuth( token, reload_timeout=auth_reload_timeout ) else: self.auth = SciTokenAuth(token)