Skip to content
Merged
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ or by cloning the repository and running:

`python setup.py install`

## Set Up
Run a one-time configuration of your login credentials in the command line using:
## Authentication

`cirro-cli configure`

This will ask you to select an authentication method. If you are a member of Fred Hutch or the University of Washington, select the default method which will give you a link to use to log through the browser. If you are not a member of those institutions, select the non-institutional authentication method and enter your Data Portal username and password into the command line when prompted.
Upon first use, the Cirro client will ask if you would like to save your login information and give you a link to authenticate through the web browser.

If you need to change your credentials after this point, and you've opted to save your login, please see the [clearing saved login](#clearing-saved-login) section.

## Command Line Usage

Expand Down Expand Up @@ -126,4 +124,9 @@ It will pause for an increasing amount of time for each retry attempt.
[General]
base_url = data-portal.io
transfer_max_retries = 15
```
```

### Clearing saved login

You can clear your saved login information by removing the `~/.cirro/token.dat` file from your system or
by running `cirro-cli configure` and selecting **No** when it asks if you'd like to save your login information.
2 changes: 1 addition & 1 deletion cirro/api/auth/oauth_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def _authenticate(client_id: str, auth_endpoint: str):
auth_status = 'authorization_pending'
while auth_status == 'authorization_pending':
time.sleep(flow['interval'])
if device_expiry < datetime.now():
if device_expiry < datetime.now().astimezone():
raise RuntimeError('Authentication timed out')

resp = requests.post(f'{auth_endpoint}/token', params=params)
Expand Down
3 changes: 3 additions & 0 deletions cirro/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def save_user_config(user_config: UserConfig):
}
if original_user_config:
ini_config['General']['base_url'] = original_user_config.base_url
ini_config['General']['transfer_max_retries'] = str(original_user_config.transfer_max_retries)

ini_config[user_config.auth_method] = user_config.auth_method_config
Constants.config_path.parent.mkdir(exist_ok=True)
Expand Down Expand Up @@ -72,6 +73,8 @@ def __init__(self, base_url: str = None):
os.environ.get('PW_BASE_URL') or
(self.user_config.base_url if self.user_config else None) or
Constants.default_base_url)
self.transfer_max_retries = self.user_config.transfer_max_retries\
if self.user_config else Constants.default_max_retries
self._init_config()

def _init_config(self):
Expand Down
2 changes: 1 addition & 1 deletion cirro/api/services/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def upload_files(self, access_context: FileAccessContext, directory: str, files:
"""
s3_client = S3Client(partial(self.get_access_credentials, access_context), self._configuration.region)
upload_directory(directory, files, s3_client, access_context.bucket, access_context.path_prefix,
max_retries=self._configuration.user_config.transfer_max_retries)
max_retries=self._configuration.transfer_max_retries)

def download_files(self, access_context: FileAccessContext, directory: str, files: List[str]):
"""
Expand Down
22 changes: 17 additions & 5 deletions cirro/cli/controller.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from cirro.api.clients.portal import DataPortalClient
from cirro.api.config import UserConfig, save_user_config
from cirro.api.config import UserConfig, save_user_config, load_user_config
from cirro.api.models.dataset import CreateIngestDatasetInput
from cirro.api.models.process import Executor
from cirro.cli.interactive.auth_args import gather_auth_config
Expand All @@ -19,8 +19,7 @@

def run_list_datasets(input_params: ListArguments, interactive=False):
"""List the datasets available in a particular project."""

# Instantiate the Cirro Data Portal client
_check_configure()
cirro = DataPortalClient()

# If the user provided the --interactive flag
Expand All @@ -47,6 +46,7 @@ def run_list_datasets(input_params: ListArguments, interactive=False):


def run_ingest(input_params: UploadArguments, interactive=False):
_check_configure()
cirro = DataPortalClient()
projects = cirro.project.list()
processes = cirro.process.list(process_type=Executor.INGEST)
Expand Down Expand Up @@ -83,6 +83,7 @@ def run_ingest(input_params: UploadArguments, interactive=False):


def run_download(input_params: DownloadArguments, interactive=False):
_check_configure()
cirro = DataPortalClient()

projects = cirro.project.list()
Expand Down Expand Up @@ -114,7 +115,7 @@ def run_download(input_params: DownloadArguments, interactive=False):

def run_configure_workflow():
"""Configure a workflow to be run in the Data Portal as a process."""

_check_configure()
cirro = DataPortalClient()
process_options = cirro.process.list(process_type=Executor.NEXTFLOW)
resources_folder, repo_prefix = get_output_resources_path()
Expand Down Expand Up @@ -171,4 +172,15 @@ def run_configure():
auth_method, auth_method_config = gather_auth_config()
save_user_config(UserConfig(auth_method=auth_method,
auth_method_config=auth_method_config,
base_url=None))
base_url=None,
transfer_max_retries=None))


def _check_configure():
"""
Prompts the user to do initial configuration if needed
"""
if load_user_config() is not None:
return

run_configure()
32 changes: 4 additions & 28 deletions cirro/cli/interactive/auth_args.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
from typing import Tuple, Dict

from cirro.api.auth import ClientAuth, UsernameAndPasswordAuth, IAMAuth
from cirro.cli.interactive.utils import ask, ask_yes_no
from cirro.cli.interactive.utils import ask_yes_no


def gather_auth_config() -> Tuple[str, Dict]:
auth_methods_map = {
'Default authentication (recommended)': ClientAuth,
'Username/password authentication (non-institutional)': UsernameAndPasswordAuth,
'AWS IAM Credentials': IAMAuth
auth_method_config = {
'enable_cache': ask_yes_no("Would you like to save your login? (do not use this on shared devices)")
}

auth_method_answer = ask('select', 'Please select and authentication method',
choices=auth_methods_map.keys())

auth_method = auth_methods_map[auth_method_answer]
auth_method_config = {}

if auth_method == UsernameAndPasswordAuth:
auth_method_config['username'] = ask('text', 'Please enter your username', required=True)
auth_method_config['password'] = ask('password', 'Please enter your password', required=True)

if auth_method == ClientAuth:
auth_method_config['enable_cache'] = ask_yes_no("Would you like to cache your login?")

if auth_method == IAMAuth:
if ask_yes_no("Would you like to manually specify AWS credentials?"):
auth_method_config['access_key'] = ask('text', 'Please enter your access key ID', required=True)
auth_method_config['secret_key'] = ask('text', 'Please enter your secret access key', required=True)
auth_method_config['token'] = ask('text', 'Please enter your session token (optional)')
else:
auth_method_config['load_current'] = True

return auth_method.__name__, auth_method_config
return 'ClientAuth', auth_method_config
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "cirro"
version = "0.6.10"
version = "0.6.11"
description = "CLI tool and SDK for interacting with the Cirro platform"
authors = ["Fred Hutch <[email protected]>"]
license = "MIT"
Expand Down