Ejemplo n.º 1
0
    def _sync_data_from_server_and_replace_local(self) -> Tuple[bool, str]:
        """
        Performs syncing of data from server and replaces local db

        Returns true for success and False for error/failure

        May raise:
        - PremiumAuthenticationError due to an UnableToDecryptRemoteData
        coming from  decompress_and_decrypt_db. This happens when the given password
        does not match the one on the saved DB.
        """
        assert self.premium, 'This function has to be called with a not None premium'
        try:
            result = self.premium.pull_data()
        except RemoteError as e:
            log.debug('sync from server -- pulling failed.', error=str(e))
            return False, f'Pulling failed: {str(e)}'

        if result['data'] is None:
            log.debug('sync from server -- no data found.')
            return False, 'No data found'

        try:
            self.data.decompress_and_decrypt_db(self.password, result['data'])
        except UnableToDecryptRemoteData:
            raise PremiumAuthenticationError(
                'The given password can not unlock the database that was retrieved  from '
                'the server. Make sure to use the same password as when the account was created.',
            )

        return True, ''
Ejemplo n.º 2
0
    def _sync_data_from_server_and_replace_local(self) -> bool:
        """
        Performs syncing of data from server and replaces local db

        Returns true for success and False for error/failure

        Can raise PremiumAuthenticationError due to an UnableToDecryptRemoteData
        coming from  decompress_and_decrypt_db. This happens when the given password
        does not match the one on the saved DB.
        """
        try:
            result = self.premium.pull_data()
        except RemoteError as e:
            log.debug('sync from server -- pulling failed.', error=str(e))
            return False

        try:
            self.data.decompress_and_decrypt_db(self.password, result['data'])
        except UnableToDecryptRemoteData:
            raise PremiumAuthenticationError(
                'The given password can not unlock the database that was retrieved  from '
                'the server. Make sure to use the same password as when the account was created.',
            )

        return True
Ejemplo n.º 3
0
    def try_premium_at_start(
            self,
            given_premium_credentials: Optional[PremiumCredentials],
            username: str,
            create_new: bool,
            sync_approval: Literal['yes', 'no', 'unknown'],
            sync_database: bool,
    ) -> Optional[Premium]:
        """
        Check if new user provided api pair or we already got one in the DB

        Returns the created premium if user's premium credentials were fine.

        If not it will raise PremiumAuthenticationError.

        If no credentials were given it returns None
        """

        if given_premium_credentials is not None:
            assert create_new, 'We should never get here for an already existing account'

            try:
                self.premium = premium_create_and_verify(given_premium_credentials)
            except PremiumAuthenticationError as e:
                self._abort_new_syncing_premium_user(username=username, original_exception=e)

        # else, if we got premium data in the DB initialize it and try to sync with the server
        db_credentials = self.data.db.get_rotkehlchen_premium()
        if db_credentials:
            assert not create_new, 'We should never get here for a new account'
            try:
                self.premium = premium_create_and_verify(db_credentials)
            except PremiumAuthenticationError as e:
                message = (
                    f'Could not authenticate with the rotkehlchen server with '
                    f'the API keys found in the Database. Error: {str(e)}'
                )
                log.error(message)
                raise PremiumAuthenticationError(message) from e

        if self.premium is None:
            return None

        result = self._can_sync_data_from_server(new_account=create_new)
        if create_new:
            # if this is a new account, make sure the api keys are properly stored
            # in the DB
            if sync_database:
                try:
                    self._sync_if_allowed(sync_approval, result)
                except PremiumAuthenticationError as e:
                    self._abort_new_syncing_premium_user(username=username, original_exception=e)

            self.data.db.set_rotkehlchen_premium(self.premium.credentials)
        else:
            self._sync_if_allowed(sync_approval, result)

        # Success, return premium
        return self.premium
Ejemplo n.º 4
0
def premium_create_and_verify(credentials: PremiumCredentials) -> Premium:
    """Create a Premium object with the key pairs and verify them.

    Returns the created premium object

    raises PremiumAuthenticationError if the given key is rejected by the server
    """
    premium = Premium(credentials)

    if premium.is_active():
        return premium

    raise PremiumAuthenticationError('Rotkehlchen API key was rejected by server')
Ejemplo n.º 5
0
 def _abort_new_syncing_premium_user(
     self,
     username: str,
     original_exception: PremiumAuthenticationError,
 ) -> None:
     """At this point we are at a new user trying to create an account with
     premium API keys and we failed. But a directory was created. Remove it.
     But create a backup of it in case something went really wrong
     and the directory contained data we did not want to lose"""
     shutil.move(
         self.data.user_data_dir,  # type: ignore
         self.data.data_directory / f'auto_backup_{username}_{ts_now()}',
     )
     raise PremiumAuthenticationError(
         f'Could not verify keys for the new account. {str(original_exception)}',  # noqa: E501
     ) from original_exception
Ejemplo n.º 6
0
    def set_credentials(self, credentials: PremiumCredentials) -> None:
        """Try to set the credentials for a premium rotkehlchen subscription

        Raises PremiumAuthenticationError if the given key is rejected by the Rotkehlchen server
        """
        old_credentials = self.credentials

        # Forget the last active value since we are trying new credentials
        self.status = SubscriptionStatus.UNKNOWN

        # If what's given is not even valid b64 encoding then stop here
        try:
            self.reset_credentials(credentials)
        except BinasciiError as e:
            raise IncorrectApiKeyFormat(f'Secret Key formatting error: {str(e)}')

        active = self.is_active()
        if not active:
            self.reset_credentials(old_credentials)
            raise PremiumAuthenticationError('Rotkehlchen API key was rejected by server')
Ejemplo n.º 7
0
    def try_premium_at_start(
        self,
        given_premium_credentials: Optional[PremiumCredentials],
        username: str,
        create_new: bool,
        sync_approval: Literal['yes', 'no', 'unknown'],
    ) -> Optional[Premium]:
        """
        Check if new user provided api pair or we already got one in the DB

        Returns the created premium if user's premium credentials were fine.

        If not it will raise PremiumAuthenticationError.

        If no credentials were given it returns None
        """

        if given_premium_credentials is not None:
            assert create_new, 'We should never get here for an already existing account'

            try:
                self.premium = premium_create_and_verify(
                    given_premium_credentials)
            except PremiumAuthenticationError as e:
                log.error('Given API key is invalid')
                # At this point we are at a new user trying to create an account with
                # premium API keys and we failed. But a directory was created. Remove it.
                # But create a backup of it in case something went really wrong
                # and the directory contained data we did not want to lose
                shutil.move(
                    self.data.user_data_dir,  # type: ignore
                    self.data.data_directory /
                    f'auto_backup_{username}_{ts_now()}',
                )
                raise PremiumAuthenticationError(
                    'Could not verify keys for the new account. '
                    '{}'.format(str(e)), )

        # else, if we got premium data in the DB initialize it and try to sync with the server
        db_credentials = self.data.db.get_rotkehlchen_premium()
        if db_credentials:
            assert not create_new, 'We should never get here for a new account'
            try:
                self.premium = premium_create_and_verify(db_credentials)
            except PremiumAuthenticationError as e:
                message = (
                    f'Could not authenticate with the rotkehlchen server with '
                    f'the API keys found in the Database. Error: {str(e)}')
                log.error(message)
                raise PremiumAuthenticationError(message)

        if self.premium is None:
            return None

        result = self._can_sync_data_from_server(new_account=create_new)
        if result.can_sync == CanSync.ASK_USER:
            if sync_approval == 'unknown':
                log.info('Remote DB is possibly newer. Ask user.')
                raise RotkehlchenPermissionError(result.message,
                                                 result.payload)
            elif sync_approval == 'yes':
                log.info('User approved data sync from server')
                if self._sync_data_from_server_and_replace_local()[0]:
                    if create_new:
                        # if we successfully synced data from the server and this is
                        # a new account, make sure the api keys are properly stored
                        # in the DB
                        self.data.db.set_rotkehlchen_premium(
                            self.premium.credentials)

            else:
                log.debug('Could sync data from server but user refused')
        elif result.can_sync == CanSync.YES:
            log.info('User approved data sync from server')
            if self._sync_data_from_server_and_replace_local()[0]:
                if create_new:
                    # if we successfully synced data from the server and this is
                    # a new account, make sure the api keys are properly stored
                    # in the DB
                    self.data.db.set_rotkehlchen_premium(
                        self.premium.credentials)

        # else result.can_sync was no, so we do nothing

        # Success, return premium
        return self.premium