Exemplo n.º 1
0
    def check_previous_upload(self, token):
        """Do API call and check for the files in the DB."""

        LOG.debug("Checking if files have been previously uploaded.")

        # Get files from db
        files = list(x for x in self.data)
        try:
            response = requests.get(
                DDSEndpoint.FILE_MATCH,
                params={"project": self.project},
                headers=token,
                json=files,
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            LOG.warning(err)
            raise SystemExit(
                "Failed to check previous upload status"
                + (
                    ": The database seems to be down."
                    if isinstance(err, requests.exceptions.ConnectionError)
                    else "."
                )
            ) from err

        if not response.ok:
            message = "Failed getting information about previously uploaded files"
            if response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR:
                raise exceptions.ApiResponseError(message=f"{message}: {response.reason}")

            raise exceptions.DDSCLIException(message=f"{message}: {response.json().get('message')}")

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

        # API failure
        if "files" not in files_in_db:
            dds_cli.utils.console.print(
                "\n:warning-emoji: Files not returned from API. :warning-emoji:\n"
            )
            os._exit(1)

        LOG.debug("Previous upload check finished.")

        return dict() if files_in_db["files"] is None else files_in_db["files"]
Exemplo n.º 2
0
    def __get_key(self, private: bool = False):
        """Get public key for project."""
        key_type = "private" if private else "public"
        # Get key from API
        try:
            response = requests.get(
                DDSEndpoint.PROJ_PRIVATE
                if private else DDSEndpoint.PROJ_PUBLIC,
                params={"project": self.project},
                headers=self.token,
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            LOG.fatal(str(err))
            raise SystemExit("Failed to get cloud information" +
                             (": The database seems to be down." if isinstance(
                                 err, requests.exceptions.ConnectionError
                             ) else ".")) from err

        if not response.ok:
            message = "Failed getting key from DDS API"
            if response.status_code == http.HTTPStatus.INTERNAL_SERVER_ERROR:
                raise exceptions.ApiResponseError(
                    message=f"{message}: {response.reason}")

            raise exceptions.DDSCLIException(
                message=f"{message}: {response.json().get('message')}")

        # Get key from response
        try:
            project_public = response.json()
        except simplejson.JSONDecodeError as err:
            LOG.fatal(str(err))
            raise SystemExit from err

        if key_type not in project_public:
            utils.console.print(
                "\n:no_entry_sign: Project access denied: No {key_type} key. :no_entry_sign:\n"
            )
            os._exit(1)

        return project_public[key_type]
Exemplo n.º 3
0
    def __get_s3_info(project_id, token):
        """Get information required to connect to cloud."""
        # Perform request to API
        try:
            response = requests.get(
                DDSEndpoint.S3KEYS,
                params={"project": project_id},
                headers=token,
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            LOG.warning(err)
            raise SystemExit("Failed to get cloud information" +
                             (": The database seems to be down." if isinstance(
                                 err, requests.exceptions.ConnectionError
                             ) else ".")) from err

        # Error
        if not response.ok:
            message = f"Connection error: {response.text}"
            raise exceptions.ApiResponseError(message)  # TODO: Change

        # Get s3 info
        s3info = utils.get_json_response(response=response)

        safespring_project, keys, url, bucket = (
            s3info.get("safespring_project"),
            s3info.get("keys"),
            s3info.get("url"),
            s3info.get("bucket"),
        )
        if None in [safespring_project, keys, url, bucket]:
            raise SystemExit(
                "Missing safespring information in response.")  # TODO: change

        return safespring_project, keys, url, bucket
Exemplo n.º 4
0
    def __authenticate_user(self):
        """Authenticates the username and password via a call to the API."""
        LOG.debug("Starting authentication on the API.")

        if self.no_prompt:
            raise exceptions.AuthenticationError(message=(
                "Authentication not possible when running with --no-prompt. "
                "Please run the `dds auth login` command and authenticate interactively."
            ))

        username = rich.prompt.Prompt.ask("DDS username")
        password = getpass.getpass(prompt="DDS password: "******"":
            raise exceptions.AuthenticationError(
                message="Non-empty password needed to be able to authenticate."
            )

        try:
            response = requests.get(
                dds_cli.DDSEndpoint.ENCRYPTED_TOKEN,
                auth=(username, password),
                timeout=dds_cli.DDSEndpoint.TIMEOUT,
            )
            response_json = response.json()
        except requests.exceptions.RequestException as err:
            raise exceptions.ApiRequestError(message=(
                "Failed to authenticate user" +
                (": The database seems to be down." if isinstance(
                    err, requests.exceptions.ConnectionError) else ".")
            )) from err
        except simplejson.JSONDecodeError as err:
            raise dds_cli.exceptions.ApiResponseError(message=str(err))

        if not response.ok:
            if response.status_code == 401:
                raise exceptions.AuthenticationError(
                    "Authentication failed, incorrect username and/or password."
                )

            raise dds_cli.exceptions.ApiResponseError(
                message=
                f"API returned an error: {response_json.get('message', 'Unknown Error!')}"
            )

        # Token received from API needs to be completed with a mfa timestamp
        partial_auth_token = response_json.get("token")

        # Verify 2fa email token
        LOG.info("Please enter the one-time authentication code sent "
                 "to your email address (leave empty to exit):")
        done = False
        while not done:
            entered_one_time_code = rich.prompt.Prompt.ask(
                "Authentication one-time code")
            if entered_one_time_code == "":
                raise exceptions.AuthenticationError(
                    message=
                    "Exited due to no one-time authentication code entered.")

            if not entered_one_time_code.isdigit():
                LOG.info(
                    "Please enter a valid one-time code. It should consist of only digits."
                )
                continue
            if len(entered_one_time_code) != 8:
                LOG.info(
                    "Please enter a valid one-time code. It should consist of 8 digits "
                    f"(you entered {len(entered_one_time_code)} digits).")
                continue

            try:
                response = requests.get(
                    dds_cli.DDSEndpoint.SECOND_FACTOR,
                    headers={"Authorization": f"Bearer {partial_auth_token}"},
                    json={"HOTP": entered_one_time_code},
                    timeout=dds_cli.DDSEndpoint.TIMEOUT,
                )
                response_json = response.json()
            except requests.exceptions.RequestException as err:
                raise exceptions.ApiRequestError(message=(
                    "Failed to authenticate with second factor" +
                    (": The database seems to be down." if isinstance(
                        err, requests.exceptions.ConnectionError) else ".")
                )) from err

            if response.ok:
                # Step out of the while-loop
                done = True
            if not response.ok:
                message = response_json.get("message", "Unexpected error!")
                if response.status_code == 401:
                    try_again = rich.prompt.Confirm.ask(
                        "Second factor authentication failed, would you like to try again?"
                    )
                    if not try_again:
                        raise exceptions.AuthenticationError(
                            message="Exited due to user choice.")
                else:
                    raise exceptions.ApiResponseError(message=message)

        # Get token from response
        token = response_json.get("token")
        if not token:
            raise exceptions.AuthenticationError(
                message="Missing token in authentication response.")

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

        return token
Exemplo n.º 5
0
    def list_projects(self, sort_by="Updated"):
        """Get a list of project(s) the user is involved in."""
        # Get projects from API
        try:
            response = requests.get(
                DDSEndpoint.LIST_PROJ,
                headers=self.token,
                json={"usage": self.show_usage},
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            raise exceptions.ApiRequestError(
                message=(
                    "Failed to get list of projects"
                    + (
                        ": The database seems to be down."
                        if isinstance(err, requests.exceptions.ConnectionError)
                        else "."
                    )
                )
            )

        # Check response
        if not response.ok:
            raise exceptions.APIError(f"Failed to get any projects: {response.text}")

        # Get result from API
        try:
            resp_json = response.json()
        except simplejson.JSONDecodeError as err:
            raise exceptions.APIError(f"Could not decode JSON response: {err}")

        # Cancel if user not involved in any projects
        usage_info = resp_json.get("total_usage")
        total_size = resp_json.get("total_size")
        project_info = resp_json.get("project_info")
        always_show = resp_json.get("always_show", False)
        if not project_info:
            raise exceptions.NoDataError("No project info was retrieved. No files to list.")

        for project in project_info:
            try:
                last_updated = pytz.timezone("UTC").localize(
                    datetime.datetime.strptime(project["Last updated"], "%a, %d %b %Y %H:%M:%S GMT")
                )
            except ValueError as err:
                raise exceptions.ApiResponseError(
                    f"Time zone mismatch: Incorrect zone '{project['Last updated'].split()[-1]}'"
                )
            else:
                project["Last updated"] = last_updated.astimezone(tzlocal.get_localzone()).strftime(
                    "%a, %d %b %Y %H:%M:%S %Z"
                )

        # Sort projects according to chosen or default, first ID
        sorted_projects = self.__sort_projects(projects=project_info, sort_by=sort_by)

        if not self.json:
            self.__print_project_table(sorted_projects, usage_info, total_size, always_show)

        # Return the list of projects
        return sorted_projects
Exemplo n.º 6
0
    def get_status(self, show_history):
        """Get current status and status history of the project."""
        try:
            response = requests.get(
                DDSEndpoint.UPDATE_PROJ_STATUS,
                headers=self.token,
                params={"project": self.project},
                json={"history": show_history},
                timeout=DDSEndpoint.TIMEOUT,
            )
        except requests.exceptions.RequestException as err:
            raise exceptions.ApiRequestError(
                message=(
                    "Failed to get project status"
                    + (
                        ": The database seems to be down."
                        if isinstance(err, requests.exceptions.ConnectionError)
                        else "."
                    )
                )
            )

        # Check response
        if not response.ok:
            raise exceptions.APIError(f"Failed to get any projects: {response.text}")

        # Get result from API
        try:
            resp_json = response.json()
        except simplejson.JSONDecodeError as err:
            raise exceptions.APIError(f"Could not decode JSON response: {err}")
        else:
            current_status = resp_json.get("current_status")
            current_deadline = resp_json.get("current_deadline")
            status_out = f"Current status of {self.project}: {current_status}"
            deadline_out = ""
            if current_deadline:
                try:
                    date = pytz.timezone("UTC").localize(
                        datetime.datetime.strptime(current_deadline, "%a, %d %b %Y %H:%M:%S GMT")
                    )
                except ValueError as err:
                    raise exceptions.ApiResponseError(
                        f"Time zone mismatch: Incorrect zone '{current_deadline.split()[-1]}'"
                    )
                else:
                    current_deadline = date.astimezone(tzlocal.get_localzone()).strftime(
                        "%a, %d %b %Y %H:%M:%S %Z"
                    )
                deadline_out = f" with deadline {current_deadline}"
            dds_cli.utils.console.print(f"{status_out}{deadline_out}")
            if show_history:
                history = "Status history \n"
                for row in resp_json.get("history"):
                    try:
                        date = pytz.timezone("UTC").localize(
                            datetime.datetime.strptime(row[1], "%a, %d %b %Y %H:%M:%S GMT")
                        )
                    except ValueError as err:
                        raise exceptions.ApiResponseError(
                            f"Time zone mismatch: Incorrect zone '{row[1].split()[-1]}'"
                        )
                    else:
                        row[1] = date.astimezone(tzlocal.get_localzone()).strftime(
                            "%a, %d %b %Y %H:%M:%S %Z"
                        )
                    history += ", ".join(list(row)) + " \n"
                LOG.info(history)