Skip to content

Commit

Permalink
Merge pull request #88 from betatim/unauthed
Browse files Browse the repository at this point in the history
[MRG] Add decorator to handle Unauthorized exceptions
  • Loading branch information
gedankenstuecke committed Jul 10, 2017
2 parents e814f2a + 144897d commit e7b7672
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
51 changes: 43 additions & 8 deletions osfclient/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
These functions implement the functionality of the command-line interface.
"""
from __future__ import print_function
from six.moves import input
import os

from functools import wraps
import getpass
import os
import sys

from six.moves import configparser
from six.moves import input

from tqdm import tqdm

from .api import OSF
from .exceptions import UnauthorizedException
from .utils import norm_remote_path, split_storage, makedirs


Expand Down Expand Up @@ -42,15 +45,20 @@ def config_from_env(config):
return config


def _get_username(args, config):
if args.username is None:
username = config.get('username')
else:
username = args.username
return username


def _setup_osf(args):
# Command line options have precedence over environment variables,
# which have precedence over the config file.
config = config_from_env(config_from_file())

if args.username is None:
username = config.get('username')
else:
username = args.username
username = _get_username(args, config)

project = config.get('project')
if args.project is None:
Expand All @@ -71,10 +79,32 @@ def _setup_osf(args):
return OSF(username=username, password=password)


def init(args):
"""Initialize or edit an existing .osfcli.config file
def might_need_auth(f):
"""Decorate a CLI function that might require authentication.
Catches any UnauthorizedException raised, prints a helpful message and
then exits.
"""
@wraps(f)
def wrapper(cli_args):
try:
return_value = f(cli_args)
except UnauthorizedException as e:
config = config_from_env(config_from_file())
username = _get_username(cli_args, config)

if username is None:
sys.exit("Please set a username (run `osf -h` for details).")
else:
sys.exit("You are not authorized to access this project.")

return return_value

return wrapper


def init(args):
"""Initialize or edit an existing .osfcli.config file."""
# reading existing config file, convert to configparser object
config = config_from_file()
config_ = configparser.ConfigParser()
Expand Down Expand Up @@ -106,6 +136,7 @@ def init(args):
cfgfile.close()


@might_need_auth
def clone(args):
"""Copy all files from all storages of a project.
Expand Down Expand Up @@ -138,6 +169,7 @@ def clone(args):
pbar.update()


@might_need_auth
def fetch(args):
"""Fetch an individual file from a project.
Expand Down Expand Up @@ -175,6 +207,7 @@ def fetch(args):
break


@might_need_auth
def list_(args):
"""List all files from all storages for project.
Expand All @@ -194,6 +227,7 @@ def list_(args):
print(os.path.join(prefix, path))


@might_need_auth
def upload(args):
"""Upload a new file to an existing project.
Expand All @@ -217,6 +251,7 @@ def upload(args):
store.create_file(remote_path, fp, update=args.force)


@might_need_auth
def remove(args):
"""Remove a file from the project's storage.
Expand Down
31 changes: 30 additions & 1 deletion osfclient/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from osfclient.tests.mocks import MockArgs

from osfclient import cli
from osfclient.exceptions import UnauthorizedException


@patch('osfclient.cli.os.path.exists', return_value=True)
Expand All @@ -19,7 +20,6 @@ def test_config_file(MockConfigParser, os_path_exists):

assert call().read('.osfcli.config') in MockConfigParser.mock_calls
assert call().items('osf') in MockConfigParser.mock_calls
print(MockConfigParser.mock_calls)


def test_config_from_env_replace_username():
Expand Down Expand Up @@ -105,3 +105,32 @@ def test_init(config_from_file):
assert call().write('username = test-user\n') in mock_open_func.mock_calls
assert call().write('project = pj2\n') in mock_open_func.mock_calls
assert call().write('[osf]\n') in mock_open_func.mock_calls


@patch('osfclient.cli.config_from_env', return_value={'username': 'tu2',
'project': 'pj2'})
def test_might_need_auth_unauthorized(config_from_file):
mock_args = MockArgs(project='test', username='theusername')

@cli.might_need_auth
def dummy(x):
raise UnauthorizedException()

with pytest.raises(SystemExit) as e:
dummy(mock_args)

assert "not authorized to access" in str(e.value)


@patch('osfclient.cli.config_from_env', return_value={'project': 'pj2'})
def test_might_need_auth_no_username(config_from_file):
mock_args = MockArgs(project='test')

@cli.might_need_auth
def dummy(x):
raise UnauthorizedException()

with pytest.raises(SystemExit) as e:
dummy(mock_args)

assert "set a username" in str(e.value)

0 comments on commit e7b7672

Please sign in to comment.