# !/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 Cherinkafrom__future__importprint_function,division,absolute_importimportnetrcimportpathlibimportwarningsfromsdss_brainimportcfg_params,logfromsdss_brain.exceptionsimportBrainError
[docs]classNetrc(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=pathorcfg_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:returnf'<Netrc(path="{self.path}", valid={self.is_valid})>'@propertydefvalid_hosts(self)->list:""" A list of valid netrc machine hosts """return[kfork,vinself._valid_hosts.items()ifv]@propertydefis_valid(self)->bool:""" Checks for a valid netrc file """try:returnself.check_netrc()exceptBrainError:returnFalse
[docs]defcheck_netrc(self)->None:""" Validates the netrc file """# check for file existenceifnotself.path.is_file():raiseBrainError(f'No .netrc file found at {self.path}!')# check for correct permissionsifoct(self.path.stat().st_mode)[-3:]!='600':raiseBrainError('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 filetry:netfile=netrc.netrc(self.path)exceptnetrc.NetrcParseErrorasnerr:raiseBrainError(f'Your netrc file was not parsed correctly. Error: {nerr}')fromnerr# check the netrc file has the allowed SDSS host machinesnethosts=netfile.hosts.keys()badlist=[]forhostinself.allowed_hosts:self._valid_hosts[host]=hostinnethostsifhostnotinnethosts:badlist.append(host)# check that the required domains are includedrequired=set(['data.sdss.org','api.sdss.org'])&set(badlist)ifrequired: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 goodreturnany(self._valid_hosts.values())
[docs]defcheck_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(hostinnetfile.hosts.keys()andhostinself._valid_hostsandself._valid_hosts[host])
[docs]defread_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 """ifnotself.check_netrc():raiseBrainError('netrc did not pass checks. Cannot read!')ifnotself.check_host(host):# change from ValueErorr raise, to issue warning and returnlog.error(f'{host} must be a valid host in the netrc')returnNone,Nonenetfile=netrc.netrc(self.path)user,acct,passwd=netfile.authenticators(host)# pylint: disable=unused-variablereturnuser,passwd