def export_card(self, repo, file_name, card_name, file_format='CSV'): """ Exports the results of a card to a new file in the repo. Any existing file with that name is overwritten. Raises PermissionDenied on insufficient privileges or bad query. """ DataHubManager.has_repo_file_privilege( self.username, self.repo_base, repo, 'write') card = Card.objects.get(repo_base=self.repo_base, repo_name=repo, card_name=card_name) query = card.query # to export a card, the user must be able to successfully execute # the query from their own database user. try: self.execute_sql(query) except Exception: raise PermissionDenied( 'Either missing required privileges or bad query') # create the user data folder if it doesn't already exist DataHubManager.create_user_data_folder(self.repo_base, repo) file_name = clean_file_name(file_name) file_path = user_data_path( self.repo_base, repo, file_name, file_format) self.user_con.export_query(query=query, file_path=file_path, file_format=file_format)
def delete_repo(self, repo, force=False): """ Deletes a repo. Pass force=True to delete the repo even if it is not empty. Returns True on success. Raises ValueError if repo has invalid characters. Raises LookupError if the repo doesn't exist. Raises InternalError if the repo is not empty and force is not True. Raises PermissionDenied if not repo_base owner. """ # Only a repo owner can delete repos. if self.repo_base != self.username: raise PermissionDenied() # remove related collaborator objects Collaborator.objects.filter( repo_name=repo, repo_base=self.repo_base).delete() # finally, delete the actual schema res = self.user_con.delete_repo(repo=repo, force=force) DataHubManager.delete_user_data_folder(self.repo_base, repo) return res
def create_card(self, repo, card_name, query): """ Creates a card in a repo from a given query. Returns the card on success. Raises IntegrityError if card with same name already exists. Raises ValueError if card name is not alphanumeric with underscores or if the card name is blank Raises PermissionDenied on insufficient privileges or bad query. """ DataHubManager.has_repo_file_privilege( self.username, self.repo_base, repo, 'write') # reject card names that have non alphanumeric characters if not re.match(r'^[A-Za-z0-9_]+$', card_name): raise ValueError( 'Only numbers, letters, and ' 'underscores are allowed in card names') # to create a card, the user must be able to successfully execute # the query from their own database user. try: self.execute_sql(query) except Exception: raise PermissionDenied( 'Either missing required privileges or bad query') card, created = Card.objects.get_or_create( repo_base=self.repo_base, repo_name=repo, card_name=card_name, query=query) return card
def create_card(self, repo, card_name, query): """ Creates a card in a repo from a given query. Returns the card on success. Raises IntegrityError if card with same name already exists. Raises PermissionDenied on insufficient privileges or bad query. """ DataHubManager.has_repo_file_privilege(self.username, self.repo_base, repo, 'write') # to create a card, the user must be able to successfully execute # the query from their own database user. try: self.execute_sql(query) except Exception: raise PermissionDenied( 'Either missing required privileges or bad query') card, created = Card.objects.get_or_create(repo_base=self.repo_base, repo_name=repo, card_name=card_name, query=query) return card
def has_repo_db_privilege(login, repo_base, repo, privilege): """ Raises PermissonDenied if user does not have the DATABASE privilege passed in the argument, e.g. 'USAGE'. Relies on database role management, so this is a pretty straightforward call. """ repo = repo.lower() repo_base = repo_base.lower() with _superuser_connection(repo_base) as conn: result = conn.has_repo_db_privilege( login=login, repo=repo, privilege=privilege) if not result: raise PermissionDenied()
def update_card(self, repo, card_name, new_query=None, new_name=None, public=None): """ Updates a card's name, query, and/or public visibility. Returns the card on success. Raises ValueError if new_name is the empty string. Raises TypeError on invalid public parameter. Raises PermissionDenied on insufficient privileges or bad new_query. """ DataHubManager.has_repo_file_privilege(self.username, self.repo_base, repo, 'write') card = Card.objects.get(repo_base=self.repo_base, repo_name=repo, card_name=card_name) # update the card if new_query is not None: # Queries for cards must work try: self.execute_sql(new_query) except Exception: raise PermissionDenied( 'Either missing required privileges or bad query') card.query = new_query if new_name is not None: if len(new_name) < 1: raise ValueError("new_name must be longer than zero " "characters") card.card_name = new_name if public is not None: if type(public) is not bool: raise TypeError("public must be of type bool") card.public = public card.save() return card
def _convert_pg_exception(e): # Convert some psycopg2 errors into exceptions meaningful to # Django. if (e.pgcode == errorcodes.INSUFFICIENT_PRIVILEGE): raise PermissionDenied() if (e.pgcode == errorcodes.INVALID_PARAMETER_VALUE or e.pgcode == errorcodes.UNDEFINED_OBJECT): raise ValueError("Invalid parameter in query.") if e.pgcode == errorcodes.INVALID_SCHEMA_NAME: error = ('Repo not found. ' 'You must specify a repo in your query. ' 'i.e. select * from REPO_NAME.TABLE_NAME. ') raise LookupError(error) if e.pgcode == errorcodes.UNDEFINED_TABLE: raise LookupError("Table or view not found.") if e.pgcode == errorcodes.DUPLICATE_SCHEMA: raise ValueError("A repo with that name already exists.") if e.pgcode == errorcodes.DUPLICATE_TABLE: raise ValueError("A table with that name already exists.") raise e
def has_repo_file_privilege(login, repo_base, repo, privilege): """ Raises PermissonDenied if user does not have the FILE privilege passed in the argument, e.g. 'read'. """ repo = repo.lower() repo_base = repo_base.lower() # Users always have privileges over their own files. if login == repo_base: return # Check if the current user or the public user has the privilege on # this repo. # The anonymous user is never explicitly shared with, so we don't need # to check for that. permitted_collaborators = Collaborator.objects.filter( repo_base=repo_base, repo_name=repo, file_permission__contains=privilege, user__username__in=[settings.PUBLIC_ROLE, login]) if not next((c for c in permitted_collaborators), None): raise PermissionDenied()
def rename_repo(self, repo, new_name): """ Renames a repo. Returns True on success. Raises ValueError if either name has invalid characters. Raises LookupError if the repo doesn't exist. Raises ValueError if the new name is taken. Raises PermissionDenied if not repo_base owner. """ # only a repo owner can rename a repo: if self.repo_base != self.username: raise PermissionDenied() # rename in user_con success = self.user_con.rename_repo(repo=repo, new_name=new_name) if success: # update collaborator(s), if there are any Collaborator.objects.filter( repo_name=repo, repo_base=self.repo_base).update( repo_name=new_name) return success