Skip to content

Commit

Permalink
Merge pull request #156 from ScilifelabDataCentre/auth_init
Browse files Browse the repository at this point in the history
Aligning CLI with the new authentication and authorization in the API
  • Loading branch information
i-oden authored Sep 21, 2021
2 parents def6b72 + 302093e commit ec8b7c1
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 417 deletions.
3 changes: 1 addition & 2 deletions dds_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ class DDSEndpoint:
)

# Authentication - user and project
AUTH = BASE_ENDPOINT + "/user/auth"
AUTH_PROJ = BASE_ENDPOINT + "/proj/auth"
TOKEN = BASE_ENDPOINT + "/user/token"

# S3Connector keys
S3KEYS = BASE_ENDPOINT + "/s3/proj"
Expand Down
45 changes: 2 additions & 43 deletions dds_cli/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
###############################################################################

# Standard library
import inspect
import logging
import os
import pathlib
import traceback

# Installed
import getpass
Expand Down Expand Up @@ -80,7 +78,7 @@ def __init__(
)

# Authenticate the user and get the token
dds_user = user.User(username=username, password=password, project=self.project)
dds_user = user.User(username=username, password=password)
self.token = dds_user.token

LOG.debug(f"Method: {self.method}, Project: {self.project}")
Expand All @@ -89,8 +87,6 @@ def __init__(
if self.method in ["put", "get"] or (
self.method in ["ls", "rm"] and self.project is not None
):
self.token = self.__verify_project_access()

if self.method in ["put", "get"]:
self.keys = self.__get_project_keys()

Expand Down Expand Up @@ -158,44 +154,6 @@ def __verify_input(

return username, password, project

def __verify_project_access(self):
"""Verifies that the user has access to the specified project."""

LOG.debug(f"Verifying access to project {self.project}...")

# Perform request to API
try:
response = requests.get(
DDSEndpoint.AUTH_PROJ,
params={"method": self.method},
headers=self.token,
timeout=DDSEndpoint.TIMEOUT,
)
except requests.exceptions.RequestException as err:
LOG.warning(err)
raise SystemExit from err

# Problem
if not response.ok:
dds_cli.utils.console.print(
f"\n:no_entry_sign: Project access denied: {response.text} :no_entry_sign:\n"
)
os._exit(1)

try:
dds_access = response.json()
except simplejson.JSONDecodeError as err:
raise SystemExit from err

# Access not granted
if not dds_access["dds-access-granted"] or "token" not in dds_access:
dds_cli.utils.console.print("\n:no_entry_sign: Project access denied :no_entry_sign:\n")
os._exit(1)

LOG.debug(f"User has been granted access to project {self.project}")

return {"x-access-token": dds_access["token"]}

def __get_project_keys(self):
"""Get public and private project keys depending on method."""

Expand All @@ -215,6 +173,7 @@ def __get_key(self, private: bool = False):
try:
response = requests.get(
DDSEndpoint.PROJ_PRIVATE if private else DDSEndpoint.PROJ_PUBLIC,
params={"project": self.project},
headers=self.token,
timeout=DDSEndpoint.TIMEOUT,
)
Expand Down
5 changes: 3 additions & 2 deletions dds_cli/data_getter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

# Own modules
from dds_cli import base
from dds_cli import DDSEndpoint, FileSegment
from dds_cli import DDSEndpoint
from dds_cli import file_handler_remote as fhr
from dds_cli import data_remover as dr
from dds_cli import file_compressor as fc
Expand Down Expand Up @@ -93,6 +93,7 @@ def __init__(
get_all=get_all,
user_input=(source, source_path_file),
token=self.token,
project=self.project,
destination=self.dds_directory.directories["FILES"],
)

Expand Down Expand Up @@ -224,7 +225,7 @@ def update_db(self, file):

# Get file info
fileinfo = self.filehandler.data[file]
params = {"name": fileinfo["name_in_db"]}
params = {"name": fileinfo["name_in_db"], "project": self.project}

# Send file info to API
try:
Expand Down
4 changes: 2 additions & 2 deletions dds_cli/data_lister.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def list_files(self, folder: str = None, show_size: bool = False):
try:
response = requests.get(
DDSEndpoint.LIST_FILES,
params={"subpath": folder, "show_size": show_size},
params={"project": self.project, "subpath": folder, "show_size": show_size},
headers=self.token,
)
except requests.exceptions.RequestException as err:
Expand Down Expand Up @@ -337,7 +337,7 @@ def _construct_file_tree(folder: str, basename: str) -> Tuple[FileTree, int, int
try:
resp_json = requests.get(
DDSEndpoint.LIST_FILES,
params={"subpath": folder, "show_size": show_size},
params={"project": self.project, "subpath": folder, "show_size": show_size},
headers=self.token,
)
except requests.exceptions.RequestException as err:
Expand Down
7 changes: 6 additions & 1 deletion dds_cli/data_putter.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def __init__(
# Get file info
self.filehandler = fhl.LocalFileHandler(
user_input=(source, source_path_file),
project=self.project,
temporary_destination=self.dds_directory.directories["FILES"],
)

Expand Down Expand Up @@ -380,6 +381,7 @@ def add_file_db(self, file):
# Get file info and specify info required in db
fileinfo = self.filehandler.data[file]
params = {
"project": self.project,
"name": file,
"name_in_bucket": fileinfo["path_remote"],
"subpath": fileinfo["subpath"],
Expand Down Expand Up @@ -426,7 +428,10 @@ def update_project_size(self):
# Perform request to DDS API
try:
response = requests.put(
DDSEndpoint.PROJECT_SIZE, headers=self.token, timeout=DDSEndpoint.TIMEOUT
DDSEndpoint.PROJECT_SIZE,
params={"project": self.project},
headers=self.token,
timeout=DDSEndpoint.TIMEOUT,
)
except requests.exceptions.RequestException as err:
# Log warning if error
Expand Down
18 changes: 15 additions & 3 deletions dds_cli/data_remover.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ def remove_all(self, *_, **__):

# Perform request to API to perform deletion
try:
response = requests.delete(DDSEndpoint.REMOVE_PROJ_CONT, headers=self.token)
response = requests.delete(
DDSEndpoint.REMOVE_PROJ_CONT, params={"project": self.project}, headers=self.token
)
except requests.exceptions.RequestException as err:
raise SystemExit from err

Expand All @@ -133,7 +135,12 @@ def remove_file(self, files):
"""Remove specific files."""

try:
response = requests.delete(DDSEndpoint.REMOVE_FILE, json=files, headers=self.token)
response = requests.delete(
DDSEndpoint.REMOVE_FILE,
params={"project": self.project},
json=files,
headers=self.token,
)
except requests.exceptions.RequestException as err:
raise SystemExit from err

Expand All @@ -153,7 +160,12 @@ def remove_folder(self, folder):
"""Remove specific folders."""

try:
response = requests.delete(DDSEndpoint.REMOVE_FOLDER, json=folder, headers=self.token)
response = requests.delete(
DDSEndpoint.REMOVE_FOLDER,
params={"project": self.project},
json=folder,
headers=self.token,
)
except requests.exceptions.RequestException as err:
raise SystemExit from err

Expand Down
3 changes: 2 additions & 1 deletion dds_cli/file_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@
class FileHandler:
"""Main file handler."""

def __init__(self, user_input, local_destination):
def __init__(self, user_input, local_destination, project=None):
source, source_path_file = user_input

# Get user specified data
self.project = project
self.local_destination = local_destination
self.data_list = []
if source is not None:
Expand Down
11 changes: 5 additions & 6 deletions dds_cli/file_handler_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,13 @@

# Installed
import requests
import rich
import simplejson
import zstandard as zstd

# Own modules
from dds_cli import DDSEndpoint
from dds_cli import file_compressor as fc
from dds_cli import file_handler as fh
from dds_cli import FileSegment
from dds_cli import status
from dds_cli.cli_decorators import subpath_required

import dds_cli.utils

Expand All @@ -42,12 +38,14 @@ class LocalFileHandler(fh.FileHandler):
"""Collects the files specified by the user."""

# Magic methods ################ Magic methods #
def __init__(self, user_input, temporary_destination):
def __init__(self, user_input, temporary_destination, project):

LOG.debug("Collecting file info...")

# Initiate FileHandler from inheritance
super().__init__(user_input=user_input, local_destination=temporary_destination)
super().__init__(
user_input=user_input, local_destination=temporary_destination, project=project
)

# Remove duplicates and save all files for later use
all_files = set(self.data_list)
Expand Down Expand Up @@ -200,6 +198,7 @@ def check_previous_upload(self, token):
try:
response = requests.get(
DDSEndpoint.FILE_MATCH,
params={"project": self.project},
headers=token,
json=files,
timeout=DDSEndpoint.TIMEOUT,
Expand Down
7 changes: 3 additions & 4 deletions dds_cli/file_handler_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@

# Installed
import requests
import rich

# Own modules
from dds_cli import DDSEndpoint
from dds_cli import file_compressor as fc
from dds_cli import file_handler as fh
import dds_cli.utils

Expand All @@ -34,10 +32,10 @@ class RemoteFileHandler(fh.FileHandler):
"""Collects the files specified by the user."""

# Magic methods ################ Magic methods #
def __init__(self, get_all, user_input, token, destination=pathlib.Path("")):
def __init__(self, get_all, user_input, token, project, destination=pathlib.Path("")):

# Initiate FileHandler from inheritance
super().__init__(user_input=user_input, local_destination=destination)
super().__init__(user_input=user_input, local_destination=destination, project=project)

self.get_all = get_all

Expand Down Expand Up @@ -84,6 +82,7 @@ def __collect_file_info_remote(self, all_paths, token):
try:
response = requests.get(
DDSEndpoint.FILE_INFO_ALL if self.get_all else DDSEndpoint.FILE_INFO,
params={"project": self.project},
headers=token,
json=all_paths,
)
Expand Down
3 changes: 1 addition & 2 deletions dds_cli/s3_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,10 @@
import logging
import os
import requests
import sys
import traceback

# Installed
import botocore
import rich
import simplejson

# Own modules
Expand Down Expand Up @@ -79,6 +77,7 @@ def get_s3_info(project_id, token):
try:
response = requests.get(
DDSEndpoint.S3KEYS,
params={"project": project_id},
headers=token,
timeout=DDSEndpoint.TIMEOUT,
)
Expand Down
20 changes: 6 additions & 14 deletions dds_cli/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,10 @@
# Standard library
import dataclasses
import logging
import os
import requests
import simplejson
import inspect

# Installed
import rich

# Own modules
from dds_cli import DDSEndpoint
import dds_cli
from dds_cli import exceptions

Expand All @@ -37,32 +31,30 @@ class User:

username: str = None
password: dataclasses.InitVar[str] = None
project: dataclasses.InitVar[str] = None
token: dict = dataclasses.field(init=False)

def __post_init__(self, password, project):
def __post_init__(self, password):
# Username and password required for user authentication
if None in [self.username, password]:
raise exceptions.MissingCredentialsException(
missing="username" if not self.username else "password",
)

# Authenticate user and get delivery JWT token
self.token = self.__authenticate_user(password=password, project=project)
self.token = self.__authenticate_user(password=password)

# Private methods ######################### Private methods #
def __authenticate_user(self, password, project):
def __authenticate_user(self, password):
"""Authenticates the username and password via a call to the API."""

LOG.debug(f"Authenticating the user: {self.username}")

# Project passed in to add it to the token. Can be None.
try:
response = requests.get(
DDSEndpoint.AUTH,
params={"project": project},
dds_cli.DDSEndpoint.TOKEN,
auth=(self.username, password),
timeout=DDSEndpoint.TIMEOUT,
timeout=dds_cli.DDSEndpoint.TIMEOUT,
)
except requests.exceptions.RequestException as err:
raise exceptions.ApiRequestError(message=str(err)) from err
Expand All @@ -89,4 +81,4 @@ def __authenticate_user(self, password, project):

LOG.debug(f"User {self.username} granted access to the DDS")

return {"x-access-token": token}
return {"Authorization": f"Bearer {token}"}
Loading

0 comments on commit ec8b7c1

Please sign in to comment.