mirror of
https://github.com/coursera-dl/coursera-dl.git
synced 2026-01-23 02:35:37 +00:00
164 lines
4.9 KiB
Python
164 lines
4.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Manages the credential information (netrc, passwords, etc).
|
|
"""
|
|
|
|
import getpass
|
|
import logging
|
|
import netrc
|
|
import os
|
|
import platform
|
|
|
|
try:
|
|
import keyring
|
|
except ImportError:
|
|
keyring = None
|
|
|
|
KEYRING_SERVICE_NAME = 'coursera-dl'
|
|
|
|
|
|
class CredentialsError(BaseException):
|
|
"""
|
|
Class to be thrown if the credentials are not found.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def _getenv_or_empty(s):
|
|
"""
|
|
Helper function that converts None gotten from the environment to the
|
|
empty string.
|
|
"""
|
|
return os.getenv(s) or ""
|
|
|
|
|
|
def get_config_paths(config_name): # pragma: no test
|
|
"""
|
|
Return a list of config files paths to try in order, given config file
|
|
name and possibly a user-specified path.
|
|
|
|
For Windows platforms, there are several paths that can be tried to
|
|
retrieve the netrc file. There is, however, no "standard way" of doing
|
|
things.
|
|
|
|
A brief recap of the situation (all file paths are written in Unix
|
|
convention):
|
|
|
|
1. By default, Windows does not define a $HOME path. However, some
|
|
people might define one manually, and many command-line tools imported
|
|
from Unix will search the $HOME environment variable first. This
|
|
includes MSYSGit tools (bash, ssh, ...) and Emacs.
|
|
|
|
2. Windows defines two 'user paths': $USERPROFILE, and the
|
|
concatenation of the two variables $HOMEDRIVE and $HOMEPATH. Both of
|
|
these paths point by default to the same location, e.g.
|
|
C:\\Users\\Username
|
|
|
|
3. $USERPROFILE cannot be changed, however $HOMEDRIVE and $HOMEPATH
|
|
can be changed. They are originally intended to be the equivalent of
|
|
the $HOME path, but there are many known issues with them
|
|
|
|
4. As for the name of the file itself, most of the tools ported from
|
|
Unix will use the standard '.dotfile' scheme, but some of these will
|
|
instead use "_dotfile". Of the latter, the two notable exceptions are
|
|
vim, which will first try '_vimrc' before '.vimrc' (but it will try
|
|
both) and git, which will require the user to name its netrc file
|
|
'_netrc'.
|
|
|
|
Relevant links :
|
|
http://markmail.org/message/i33ldu4xl5aterrr
|
|
http://markmail.org/message/wbzs4gmtvkbewgxi
|
|
http://stackoverflow.com/questions/6031214/
|
|
|
|
Because the whole thing is a mess, I suggest we tried various sensible
|
|
defaults until we succeed or have depleted all possibilities.
|
|
"""
|
|
|
|
if platform.system() != 'Windows':
|
|
return [None]
|
|
|
|
# Now, we only treat the case of Windows
|
|
env_vars = [["HOME"],
|
|
["HOMEDRIVE", "HOMEPATH"],
|
|
["USERPROFILE"],
|
|
["SYSTEMDRIVE"]]
|
|
|
|
env_dirs = []
|
|
for var_list in env_vars:
|
|
|
|
var_values = [_getenv_or_empty(var) for var in var_list]
|
|
|
|
directory = ''.join(var_values)
|
|
if not directory:
|
|
logging.debug('Environment var(s) %s not defined, skipping',
|
|
var_list)
|
|
else:
|
|
env_dirs.append(directory)
|
|
|
|
additional_dirs = ["C:", ""]
|
|
|
|
all_dirs = env_dirs + additional_dirs
|
|
|
|
leading_chars = [".", "_"]
|
|
|
|
res = [''.join([directory, os.sep, lc, config_name])
|
|
for directory in all_dirs
|
|
for lc in leading_chars]
|
|
|
|
return res
|
|
|
|
|
|
def authenticate_through_netrc(path=None):
|
|
"""
|
|
Return the tuple user / password given a path for the .netrc file.
|
|
|
|
Raises CredentialsError if no valid netrc file is found.
|
|
"""
|
|
errors = []
|
|
netrc_machine = 'coursera-dl'
|
|
paths = [path] if path else get_config_paths("netrc")
|
|
for path in paths:
|
|
try:
|
|
logging.debug('Trying netrc file %s', path)
|
|
auths = netrc.netrc(path).authenticators(netrc_machine)
|
|
except (IOError, netrc.NetrcParseError) as e:
|
|
errors.append(e)
|
|
else:
|
|
if auths is None:
|
|
errors.append('Didn\'t find any credentials for ' +
|
|
netrc_machine)
|
|
else:
|
|
return auths[0], auths[2]
|
|
|
|
error_messages = '\n'.join(str(e) for e in errors)
|
|
raise CredentialsError(
|
|
'Did not find valid netrc file:\n' + error_messages +
|
|
'\nPlease run this command: chmod og-rw ~/.netrc')
|
|
|
|
|
|
def get_credentials(username=None, password=None, netrc=None, use_keyring=False):
|
|
"""
|
|
Return valid username, password tuple.
|
|
|
|
Raises CredentialsError if username or password is missing.
|
|
"""
|
|
if netrc:
|
|
path = None if netrc is True else netrc
|
|
return authenticate_through_netrc(path)
|
|
|
|
if not username:
|
|
raise CredentialsError(
|
|
'Please provide a username with the -u option, '
|
|
'or a .netrc file with the -n option.')
|
|
|
|
if not password and use_keyring:
|
|
password = keyring.get_password(KEYRING_SERVICE_NAME, username)
|
|
|
|
if not password:
|
|
password = getpass.getpass('Coursera password for {0}: '.format(username))
|
|
if use_keyring:
|
|
keyring.set_password(KEYRING_SERVICE_NAME, username, password)
|
|
|
|
return username, password
|