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, ''
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
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
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')
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
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')
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