Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: use per-user db role for query exec (ENG-2474)
Login with per-user PG database role, if available, to ensure that RLS
policies are applied to user queries.
  • Loading branch information
johnsca committed Aug 11, 2023
commit 41a87de6456ede479d714f53f9754e3dd9302164
27 changes: 23 additions & 4 deletions redash/query_runner/pg.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import hashlib
import os
import logging
import select
Expand All @@ -9,6 +10,7 @@
import psycopg2
from psycopg2.extras import Range

from redash import settings
from redash.query_runner import *
from redash.utils import JSONEncoder, json_dumps, json_loads

Expand Down Expand Up @@ -238,12 +240,29 @@ def _get_tables(self, schema):

return list(schema.values())

def _gen_role_pass(self, role_name: str) -> str:
"""
Generate a password for a given role using the datasource secret and role name.
"""
secret = settings.DATASOURCE_SECRET_KEY
return hashlib.sha256(f"{secret}:{role_name}".encode("utf-8")).hexdigest()

@inject_iam_auth
def _get_connection(self):
def _get_connection(self, user):
if getattr(user, "db_role", None):
auth_config = dict(
user=user.db_role,
password=self._gen_role_pass(user.db_role),
)
else:
auth_config = dict(
user=self.configuration.get("user"),
password=self.configuration.get("password"),
)
logger.info(f"Connecting to datasource as {auth_config['user']}")
self.ssl_config = _get_ssl_config(self.configuration)
connection = psycopg2.connect(
user=self.configuration.get("user"),
password=self.configuration.get("password"),
**auth_config,
host=self.configuration.get("host"),
port=self.configuration.get("port"),
dbname=self.configuration.get("dbname"),
Expand All @@ -254,7 +273,7 @@ def _get_connection(self):
return connection

def run_query(self, query, user):
connection = self._get_connection()
connection = self._get_connection(user)
_wait(connection, timeout=10)

cursor = connection.cursor()
Expand Down