Source code for podpac.core.authentication

"""
PODPAC Authentication
"""


import getpass
import logging

import requests
import traitlets as tl
from lazy_import import lazy_module, lazy_function

from podpac.core.settings import settings
from podpac.core.utils import cached_property

# Optional dependencies
pydap_setup_session = lazy_function("pydap.cas.urs.setup_session")

_log = logging.getLogger(__name__)


def set_credentials(hostname, uname=None, password=None):
    """Set authentication credentials for a remote URL in the :class:`podpac.settings`.

    Parameters
    ----------
    hostname : str
        Hostname for `uname` and `password`.
    uname : str, optional
        Username to store in settings for `hostname`.
        If no username is provided and the username does not already exist in the settings,
        the user will be prompted to enter one.
    password : str, optional
        Password to store in settings for `hostname`
        If no password is provided and the password does not already exist in the settings,
        the user will be prompted to enter one.
    """

    if hostname is None or hostname == "":
        raise ValueError("`hostname` must be defined")

    # see whats stored in settings already
    u_settings = settings.get("username@{}".format(hostname))
    p_settings = settings.get("password@{}".format(hostname))

    # get username from 1. function input 2. settings 3. python input()
    u = uname or u_settings or getpass.getpass("Username: ")
    p = password or p_settings or getpass.getpass()

    # set values in settings
    settings["username@{}".format(hostname)] = u
    settings["password@{}".format(hostname)] = p

    _log.debug("Set credentials for hostname {}".format(hostname))


[docs]class RequestsSessionMixin(tl.HasTraits): hostname = tl.Unicode(allow_none=False) auth_required = tl.Bool(default_value=False) @property def username(self): """Returns username stored in settings for accessing `self.hostname`. The username is stored under key `username@<hostname>` Returns ------- str username stored in settings for accessing `self.hostname` Raises ------ ValueError Raises a ValueError if not username is stored in settings for `self.hostname` """ key = "username@{}".format(self.hostname) username = settings.get(key) if not username: raise ValueError( "No username found for hostname '{0}'. Use `{1}.set_credentials(username='<username>', password='<password>') to store credentials for this host".format( self.hostname, self.__class__.__name__ ) ) return username @property def password(self): """Returns password stored in settings for accessing `self.hostname`. The password is stored under key `password@<hostname>` Returns ------- str password stored in settings for accessing `self.hostname` Raises ------ ValueError Raises a ValueError if not password is stored in settings for `self.hostname` """ key = "password@{}".format(self.hostname) password = settings.get(key) if not password: raise ValueError( "No password found for hostname {0}. Use `{1}.set_credentials(username='<username>', password='<password>') to store credentials for this host".format( self.hostname, self.__class__.__name__ ) ) return password @cached_property def session(self): """Requests Session object for making calls to remote `self.hostname` See https://2.python-requests.org/en/master/api/#sessionapi Returns ------- :class:requests.Session Requests Session class with `auth` attribute defined """ return self._create_session()
[docs] def set_credentials(self, username=None, password=None): """Shortcut to :func:`podpac.authentication.set_crendentials` using class member :attr:`self.hostname` for the hostname Parameters ---------- username : str, optional Username to store in settings for `self.hostname`. If no username is provided and the username does not already exist in the settings, the user will be prompted to enter one. password : str, optional Password to store in settings for `self.hostname` If no password is provided and the password does not already exist in the settings, the user will be prompted to enter one. """ return set_credentials(self.hostname, uname=username, password=password)
def _create_session(self): """Creates a :class:`requests.Session` with username and password defined Returns ------- :class:`requests.Session` """ s = requests.Session() try: s.auth = (self.username, self.password) except ValueError as e: if self.auth_required: raise e else: _log.warning("No auth provided for session") return s
class NASAURSSessionMixin(RequestsSessionMixin): check_url = tl.Unicode() hostname = tl.Unicode(default_value="urs.earthdata.nasa.gov") auth_required = tl.Bool(True) def _create_session(self): """Creates an authenticated :class:`requests.Session` with username and password defined Returns ------- :class:`requests.Session` Notes ----- The session is authenticated against the user-provided self.check_url """ try: s = pydap_setup_session(self.username, self.password, check_url=self.check_url) except ValueError as e: if self.auth_required: raise e else: _log.warning("No auth provided for session") return s
[docs]class S3Mixin(tl.HasTraits): """Mixin to add S3 credentials and access to a Node.""" anon = tl.Bool(False).tag(attr=True) aws_access_key_id = tl.Unicode(allow_none=True) aws_secret_access_key = tl.Unicode(allow_none=True) aws_region_name = tl.Unicode(allow_none=True) aws_client_kwargs = tl.Dict() config_kwargs = tl.Dict() aws_requester_pays = tl.Bool(False) @tl.default("aws_access_key_id") def _get_access_key_id(self): return settings["AWS_ACCESS_KEY_ID"] @tl.default("aws_secret_access_key") def _get_secret_access_key(self): return settings["AWS_SECRET_ACCESS_KEY"] @tl.default("aws_region_name") def _get_region_name(self): return settings["AWS_REGION_NAME"] @tl.default("aws_requester_pays") def _get_requester_pays(self): return settings["AWS_REQUESTER_PAYS"] @cached_property def s3(self): # this has to be done here for multithreading to work s3fs = lazy_module("s3fs") if self.anon: return s3fs.S3FileSystem(anon=True, client_kwargs=self.aws_client_kwargs) else: return s3fs.S3FileSystem( key=self.aws_access_key_id, secret=self.aws_secret_access_key, client_kwargs=self.aws_client_kwargs, config_kwargs=self.config_kwargs, requester_pays=self.aws_requester_pays, )