Source code for sdss_brain.auth.netrc

# !/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Filename: netrc.py
# Project: auth
# Author: Brian Cherinka
# Created: Wednesday, 21st October 2020 2:52:15 pm
# License: BSD 3-clause "New" or "Revised" License
# Copyright (c) 2020 Brian Cherinka
# Last Modified: Wednesday, 21st October 2020 2:52:15 pm
# Modified By: Brian Cherinka


from __future__ import print_function, division, absolute_import

import netrc
import pathlib
import warnings
from sdss_brain import cfg_params
from sdss_brain.exceptions import BrainError


[docs]class Netrc(object): """ Class representing a netrc file This class represents a local .netrc file, used for authenticating users against remote machines. This class validates a netrc file for existence and correct permissions. It also checks its hosts against the allowed SDSS host domains data.sdss.org and api.sdss.org and ensures their presence. Parameters ---------- path : str The path to a .netrc file. Defaults to custom "netrc_path" config path or ~/.netrc """ def __init__(self, path: str = None): path = path or cfg_params.get('netrc_path', None) or '~/.netrc' self.path = pathlib.Path(path).expanduser() self.allowed_hosts = ['data.sdss.org', 'api.sdss.org', 'magrathea.sdss.org', 'data.sdss5.org', 'wiki.sdss.org'] self._valid_hosts = {} self.check_netrc() def __repr__(self) -> str: return f'<Netrc(path="{self.path}", valid={self.is_valid})>' @property def valid_hosts(self) -> list: """ A list of valid netrc machine hosts """ return [k for k, v in self._valid_hosts.items() if v] @property def is_valid(self) -> bool: """ Checks for a valid netrc file """ try: return self.check_netrc() except BrainError: return False
[docs] def check_netrc(self) -> None: """ Validates the netrc file """ # check for file existence if not self.path.is_file(): raise BrainError(f'No .netrc file found at {self.path}!') # check for correct permissions if oct(self.path.stat().st_mode)[-3:] != '600': raise BrainError('Your .netrc file does not have 600 permissions. Please fix it by ' 'running chmod 600 on it. Authentication will not work with ' 'permissions different from 600.') # read the netrc file try: netfile = netrc.netrc(self.path) except netrc.NetrcParseError as nerr: raise BrainError(f'Your netrc file was not parsed correctly. Error: {nerr}') from nerr # check the netrc file has the allowed SDSS host machines nethosts = netfile.hosts.keys() badlist = [] for host in self.allowed_hosts: self._valid_hosts[host] = host in nethosts if host not in nethosts: badlist.append(host) # check that the required domains are included required = set(['data.sdss.org', 'api.sdss.org']) & set(badlist) if required: warnings.warn(f"Hosts {', '.join(required)} not found in netrc. " "You will not be able to remotely access SDSS data.", UserWarning) # validate if any are good return any(self._valid_hosts.values())
[docs] def check_host(self, host: str) -> bool: """ Checks the host against the local netrc file Checks if the host is a valid machine in the netrc file Parameters ---------- host : str The netrc machine name Returns ------- bool True if the host is valid """ netfile = netrc.netrc(self.path) return (host in netfile.hosts.keys() and host in self._valid_hosts and self._valid_hosts[host])
[docs] def read_netrc(self, host: str) -> tuple: """ Read the netrc file for a given host Reads the username, password for the given host machine. Note this returns plaintext username and password. Do not write out these values some place transparent and publicly visible. Parameters ---------- host : str The netrc machine name Returns ------- tuple Plain text netrc username, password Raises ------ BrainError when netrc file fails to pass checks ValueError when input host is not valid """ if not self.check_netrc(): raise BrainError('netrc did not pass checks. Cannot read!') if not self.check_host(host): raise ValueError(f'{host} must be a valid host in the netrc') netfile = netrc.netrc(self.path) user, acct, passwd = netfile.authenticators(host) # pylint: disable=unused-variable return user, passwd