redash/settings/__init__.py (384 lines of code) (raw):
import os
import importlib
import ssl
from funcy import distinct, remove
from flask_talisman import talisman
from dotenv import load_dotenv
load_dotenv()
from .helpers import (
fix_assets_path,
array_from_string,
parse_boolean,
int_or_none,
set_from_string,
add_decode_responses_to_redis_url,
cast_int_or_default
)
from .organization import DATE_FORMAT, TIME_FORMAT # noqa
# _REDIS_URL is the unchanged REDIS_URL we get from env vars, to be used later with RQ
_REDIS_URL = os.environ.get(
"REDASH_REDIS_URL", os.environ.get("REDIS_URL", "redis://localhost:6379/0")
)
# This is the one to use for Redash' own connection:
REDIS_URL = add_decode_responses_to_redis_url(_REDIS_URL)
PROXIES_COUNT = int(os.environ.get("REDASH_PROXIES_COUNT", "1"))
STATSD_HOST = os.environ.get("REDASH_STATSD_HOST", "127.0.0.1")
STATSD_PORT = int(os.environ.get("REDASH_STATSD_PORT", "8125"))
STATSD_PREFIX = os.environ.get("REDASH_STATSD_PREFIX", "redash")
STATSD_USE_TAGS = parse_boolean(os.environ.get("REDASH_STATSD_USE_TAGS", "false"))
# Connection settings for Redash's own database (where we store the queries, results, etc)
SQLALCHEMY_DATABASE_URI = os.environ.get(
"REDASH_DATABASE_URL", os.environ.get("DATABASE_URL", "postgresql:///postgres")
)
SQLALCHEMY_MAX_OVERFLOW = int_or_none(os.environ.get("SQLALCHEMY_MAX_OVERFLOW"))
SQLALCHEMY_POOL_SIZE = int_or_none(os.environ.get("SQLALCHEMY_POOL_SIZE"))
SQLALCHEMY_DISABLE_POOL = parse_boolean(
os.environ.get("SQLALCHEMY_DISABLE_POOL", "false")
)
SQLALCHEMY_ENABLE_POOL_PRE_PING = parse_boolean(
os.environ.get("SQLALCHEMY_ENABLE_POOL_PRE_PING", "false")
)
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
RQ_REDIS_URL = os.environ.get("RQ_REDIS_URL", _REDIS_URL)
# The following enables periodic job (every 5 minutes) of removing unused query results.
QUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(
os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_ENABLED", "true")
)
QUERY_RESULTS_CLEANUP_COUNT = int(
os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_COUNT", "100")
)
QUERY_RESULTS_CLEANUP_MAX_AGE = int(
os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_MAX_AGE", "7")
)
SCHEMAS_REFRESH_SCHEDULE = int(os.environ.get("REDASH_SCHEMAS_REFRESH_SCHEDULE", 30))
AUTH_TYPE = os.environ.get("REDASH_AUTH_TYPE", "api_key")
INVITATION_TOKEN_MAX_AGE = int(
os.environ.get("REDASH_INVITATION_TOKEN_MAX_AGE", 60 * 60 * 24 * 7)
)
# The secret key to use in the Flask app for various cryptographic features
SECRET_KEY = os.environ.get("REDASH_COOKIE_SECRET")
if SECRET_KEY is None:
raise Exception("You must set the REDASH_COOKIE_SECRET environment variable. Visit http://redash.io/help/open-source/admin-guide/secrets for more information.")
# The secret key to use when encrypting data source options
DATASOURCE_SECRET_KEY = os.environ.get("REDASH_SECRET_KEY", SECRET_KEY)
# Whether and how to redirect non-HTTP requests to HTTPS. Disabled by default.
ENFORCE_HTTPS = parse_boolean(os.environ.get("REDASH_ENFORCE_HTTPS", "false"))
ENFORCE_HTTPS_PERMANENT = parse_boolean(
os.environ.get("REDASH_ENFORCE_HTTPS_PERMANENT", "false")
)
# Whether file downloads are enforced or not.
ENFORCE_FILE_SAVE = parse_boolean(os.environ.get("REDASH_ENFORCE_FILE_SAVE", "true"))
# Whether api calls using the json query runner will block private addresses
ENFORCE_PRIVATE_ADDRESS_BLOCK = parse_boolean(
os.environ.get("REDASH_ENFORCE_PRIVATE_IP_BLOCK", "true")
)
# Whether to use secure cookies by default.
COOKIES_SECURE = parse_boolean(
os.environ.get("REDASH_COOKIES_SECURE", str(ENFORCE_HTTPS))
)
# Whether the session cookie is set to secure.
SESSION_COOKIE_SECURE = parse_boolean(
os.environ.get("REDASH_SESSION_COOKIE_SECURE") or str(COOKIES_SECURE)
)
SESSION_COOKIE_NAME = os.environ.get('SESSION_COOKIE_NAME', 'redash-session')
# Whether the session cookie is set HttpOnly.
SESSION_COOKIE_HTTPONLY = parse_boolean(
os.environ.get("REDASH_SESSION_COOKIE_HTTPONLY", "true")
)
SESSION_EXPIRY_TIME = int(os.environ.get("REDASH_SESSION_EXPIRY_TIME", 60 * 60 * 6))
# Whether the session cookie is set to secure.
REMEMBER_COOKIE_SECURE = parse_boolean(
os.environ.get("REDASH_REMEMBER_COOKIE_SECURE") or str(COOKIES_SECURE)
)
# Whether the remember cookie is set HttpOnly.
REMEMBER_COOKIE_HTTPONLY = parse_boolean(
os.environ.get("REDASH_REMEMBER_COOKIE_HTTPONLY", "true")
)
# The amount of time before the remember cookie expires.
REMEMBER_COOKIE_DURATION = int(
os.environ.get("REDASH_REMEMBER_COOKIE_DURATION", 60 * 60 * 24 * 31)
)
SESSION_REFRESH_EACH_REQUEST = parse_boolean(
os.environ.get("REDASH_SESSION_REFRESH_EACH_REQUEST", "false")
)
# Doesn't set X-Frame-Options by default since it's highly dependent
# on the specific deployment.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
# for more information.
FRAME_OPTIONS = os.environ.get("REDASH_FRAME_OPTIONS", "deny")
FRAME_OPTIONS_ALLOW_FROM = os.environ.get("REDASH_FRAME_OPTIONS_ALLOW_FROM", "")
# Whether and how to send Strict-Transport-Security response headers.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
# for more information.
HSTS_ENABLED = parse_boolean(
os.environ.get("REDASH_HSTS_ENABLED") or str(ENFORCE_HTTPS)
)
HSTS_PRELOAD = parse_boolean(os.environ.get("REDASH_HSTS_PRELOAD", "false"))
HSTS_MAX_AGE = int(os.environ.get("REDASH_HSTS_MAX_AGE", talisman.ONE_YEAR_IN_SECS))
HSTS_INCLUDE_SUBDOMAINS = parse_boolean(
os.environ.get("REDASH_HSTS_INCLUDE_SUBDOMAINS", "false")
)
# Whether and how to send Content-Security-Policy response headers.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
# for more information.
# Overriding this value via an environment variables requires setting it
# as a string in the general CSP format of a semicolon separated list of
# individual CSP directives, see https://github.com/GoogleCloudPlatform/flask-talisman#example-7
# for more information. E.g.:
CONTENT_SECURITY_POLICY = os.environ.get(
"REDASH_CONTENT_SECURITY_POLICY",
"default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; font-src 'self' data:; img-src 'self' http: https: data: blob:; object-src 'none'; frame-ancestors 'none'; frame-src redash.io;",
)
CONTENT_SECURITY_POLICY_REPORT_URI = os.environ.get(
"REDASH_CONTENT_SECURITY_POLICY_REPORT_URI", ""
)
CONTENT_SECURITY_POLICY_REPORT_ONLY = parse_boolean(
os.environ.get("REDASH_CONTENT_SECURITY_POLICY_REPORT_ONLY", "false")
)
CONTENT_SECURITY_POLICY_NONCE_IN = array_from_string(
os.environ.get("REDASH_CONTENT_SECURITY_POLICY_NONCE_IN", "")
)
# Whether and how to send Referrer-Policy response headers. Defaults to
# 'strict-origin-when-cross-origin'.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
# for more information.
REFERRER_POLICY = os.environ.get(
"REDASH_REFERRER_POLICY", "strict-origin-when-cross-origin"
)
# Whether and how to send Feature-Policy response headers. Defaults to
# an empty value.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
# for more information.
FEATURE_POLICY = os.environ.get("REDASH_REFERRER_POLICY", "")
MULTI_ORG = parse_boolean(os.environ.get("REDASH_MULTI_ORG", "false"))
ROUTE_PREFIX = os.getenv('REDASH_ROUTE_PREFIX')
if ROUTE_PREFIX and ROUTE_PREFIX[0:1] != "/":
ROUTE_PREFIX = "/" + ROUTE_PREFIX
GOOGLE_CLIENT_ID = os.environ.get("REDASH_GOOGLE_CLIENT_ID", "")
GOOGLE_CLIENT_SECRET = os.environ.get("REDASH_GOOGLE_CLIENT_SECRET", "")
GOOGLE_OAUTH_ENABLED = bool(GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET)
# If Redash is behind a proxy it might sometimes receive a X-Forwarded-Proto of HTTP
# even if your actual Redash URL scheme is HTTPS. This will cause Flask to build
# the SAML redirect URL incorrect thus failing auth. This is especially common if
# you're behind a SSL/TCP configured AWS ELB or similar.
# This setting will force the URL scheme.
SAML_SCHEME_OVERRIDE = os.environ.get("REDASH_SAML_SCHEME_OVERRIDE", "")
SAML_ENCRYPTION_PEM_PATH = os.environ.get("REDASH_SAML_ENCRYPTION_PEM_PATH", "")
SAML_ENCRYPTION_CERT_PATH = os.environ.get("REDASH_SAML_ENCRYPTION_CERT_PATH", "")
SAML_ENCRYPTION_ENABLED = SAML_ENCRYPTION_PEM_PATH != "" and SAML_ENCRYPTION_CERT_PATH != ""
# Enables the use of an externally-provided and trusted remote user via an HTTP
# header. The "user" must be an email address.
#
# By default the trusted header is X-Forwarded-Remote-User. You can change
# this by setting REDASH_REMOTE_USER_HEADER.
#
# Enabling this authentication method is *potentially dangerous*, and it is
# your responsibility to ensure that only a trusted frontend (usually on the
# same server) can talk to the redash backend server, otherwise people will be
# able to login as anyone they want by directly talking to the redash backend.
# You must *also* ensure that any special header in the original request is
# removed or always overwritten by your frontend, otherwise your frontend may
# pass it through to the backend unchanged.
#
# Note that redash will only check the remote user once, upon the first need
# for a login, and then set a cookie which keeps the user logged in. Dropping
# the remote user header after subsequent requests won't automatically log the
# user out. Doing so could be done with further work, but usually it's
# unnecessary.
#
# If you also set the organization setting auth_password_login_enabled to false,
# then your authentication will be seamless. Otherwise a link will be presented
# on the login page to trigger remote user auth.
REMOTE_USER_LOGIN_ENABLED = parse_boolean(
os.environ.get("REDASH_REMOTE_USER_LOGIN_ENABLED", "false")
)
REMOTE_USER_HEADER = os.environ.get(
"REDASH_REMOTE_USER_HEADER", "X-Forwarded-Remote-User"
)
# If the organization setting auth_password_login_enabled is not false, then users will still be
# able to login through Redash instead of the LDAP server
LDAP_LOGIN_ENABLED = parse_boolean(os.environ.get("REDASH_LDAP_LOGIN_ENABLED", "false"))
# Bind LDAP using SSL. Default is False
LDAP_SSL = parse_boolean(os.environ.get("REDASH_LDAP_USE_SSL", "false"))
# Choose authentication method(SIMPLE, ANONYMOUS or NTLM). Default is SIMPLE
LDAP_AUTH_METHOD = os.environ.get("REDASH_LDAP_AUTH_METHOD", "SIMPLE")
# The LDAP directory address (ex. ldap://10.0.10.1:389)
LDAP_HOST_URL = os.environ.get("REDASH_LDAP_URL", None)
# The DN & password used to connect to LDAP to determine the identity of the user being authenticated.
# For AD this should be "org\\user".
LDAP_BIND_DN = os.environ.get("REDASH_LDAP_BIND_DN", None)
LDAP_BIND_DN_PASSWORD = os.environ.get("REDASH_LDAP_BIND_DN_PASSWORD", "")
# AD/LDAP email and display name keys
LDAP_DISPLAY_NAME_KEY = os.environ.get("REDASH_LDAP_DISPLAY_NAME_KEY", "displayName")
LDAP_EMAIL_KEY = os.environ.get("REDASH_LDAP_EMAIL_KEY", "mail")
# Prompt that should be shown above username/email field.
LDAP_CUSTOM_USERNAME_PROMPT = os.environ.get(
"REDASH_LDAP_CUSTOM_USERNAME_PROMPT", "LDAP/AD/SSO username:"
)
# LDAP Search DN TEMPLATE (for AD this should be "(sAMAccountName=%(username)s)"")
LDAP_SEARCH_TEMPLATE = os.environ.get(
"REDASH_LDAP_SEARCH_TEMPLATE", "(cn=%(username)s)"
)
# The schema to bind to (ex. cn=users,dc=ORG,dc=local)
LDAP_SEARCH_DN = os.environ.get(
"REDASH_LDAP_SEARCH_DN", os.environ.get("REDASH_SEARCH_DN")
)
STATIC_ASSETS_PATH = fix_assets_path(
os.environ.get("REDASH_STATIC_ASSETS_PATH", "../client/dist/")
)
FLASK_TEMPLATE_PATH = fix_assets_path(
os.environ.get("REDASH_FLASK_TEMPLATE_PATH", STATIC_ASSETS_PATH)
)
# Time limit (in seconds) for scheduled queries. Set this to -1 to execute without a time limit.
SCHEDULED_QUERY_TIME_LIMIT = int(
os.environ.get("REDASH_SCHEDULED_QUERY_TIME_LIMIT", -1)
)
# Time limit (in seconds) for adhoc queries. Set this to -1 to execute without a time limit.
ADHOC_QUERY_TIME_LIMIT = int(os.environ.get("REDASH_ADHOC_QUERY_TIME_LIMIT", -1))
JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600 * 12))
JOB_DEFAULT_FAILURE_TTL = int(
os.environ.get("REDASH_JOB_DEFAULT_FAILURE_TTL", 7 * 24 * 60 * 60)
)
LOG_LEVEL = os.environ.get("REDASH_LOG_LEVEL", "INFO")
LOG_STDOUT = parse_boolean(os.environ.get("REDASH_LOG_STDOUT", "false"))
LOG_PREFIX = os.environ.get("REDASH_LOG_PREFIX", "")
LOG_FORMAT = os.environ.get(
"REDASH_LOG_FORMAT",
LOG_PREFIX + "[%(asctime)s][PID:%(process)d][%(levelname)s][%(name)s] %(message)s",
)
RQ_WORKER_JOB_LOG_FORMAT = os.environ.get(
"REDASH_RQ_WORKER_JOB_LOG_FORMAT",
(
LOG_PREFIX + "[%(asctime)s][PID:%(process)d][%(levelname)s][%(name)s] "
"job.func_name=%(job_func_name)s "
"job.id=%(job_id)s %(message)s"
),
)
# Mail settings:
MAIL_SERVER = os.environ.get("REDASH_MAIL_SERVER", "localhost")
MAIL_PORT = int(os.environ.get("REDASH_MAIL_PORT", 25))
MAIL_USE_TLS = parse_boolean(os.environ.get("REDASH_MAIL_USE_TLS", "false"))
MAIL_USE_SSL = parse_boolean(os.environ.get("REDASH_MAIL_USE_SSL", "false"))
MAIL_USERNAME = os.environ.get("REDASH_MAIL_USERNAME", None)
MAIL_PASSWORD = os.environ.get("REDASH_MAIL_PASSWORD", None)
MAIL_DEFAULT_SENDER = os.environ.get("REDASH_MAIL_DEFAULT_SENDER", None)
MAIL_MAX_EMAILS = os.environ.get("REDASH_MAIL_MAX_EMAILS", None)
MAIL_ASCII_ATTACHMENTS = parse_boolean(
os.environ.get("REDASH_MAIL_ASCII_ATTACHMENTS", "false")
)
def email_server_is_configured():
return MAIL_DEFAULT_SENDER is not None
HOST = os.environ.get("REDASH_HOST", "")
SEND_FAILURE_EMAIL_INTERVAL = int(
os.environ.get("REDASH_SEND_FAILURE_EMAIL_INTERVAL", 60)
)
MAX_FAILURE_REPORTS_PER_QUERY = int(
os.environ.get("REDASH_MAX_FAILURE_REPORTS_PER_QUERY", 100)
)
ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE = os.environ.get(
"REDASH_ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE", "({state}) {alert_name}"
)
# How many requests are allowed per IP to the login page before
# being throttled?
# See https://flask-limiter.readthedocs.io/en/stable/#rate-limit-string-notation
RATELIMIT_ENABLED = parse_boolean(os.environ.get("REDASH_RATELIMIT_ENABLED", "true"))
THROTTLE_LOGIN_PATTERN = os.environ.get("REDASH_THROTTLE_LOGIN_PATTERN", "50/hour")
LIMITER_STORAGE = os.environ.get("REDASH_LIMITER_STORAGE", REDIS_URL)
THROTTLE_PASS_RESET_PATTERN = os.environ.get("REDASH_THROTTLE_PASS_RESET_PATTERN", "10/hour")
# CORS settings for the Query Result API (and possibly future external APIs).
# In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN
# to the calling domain (or domains in a comma separated list).
ACCESS_CONTROL_ALLOW_ORIGIN = set_from_string(
os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN", "")
)
ACCESS_CONTROL_ALLOW_CREDENTIALS = parse_boolean(
os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_CREDENTIALS", "false")
)
ACCESS_CONTROL_REQUEST_METHOD = os.environ.get(
"REDASH_CORS_ACCESS_CONTROL_REQUEST_METHOD", "GET, POST, PUT"
)
ACCESS_CONTROL_ALLOW_HEADERS = os.environ.get(
"REDASH_CORS_ACCESS_CONTROL_ALLOW_HEADERS", "Content-Type"
)
# Query Runners
default_query_runners = [
"redash.query_runner.athena",
"redash.query_runner.big_query",
"redash.query_runner.google_spreadsheets",
"redash.query_runner.graphite",
"redash.query_runner.mongodb",
"redash.query_runner.couchbase",
"redash.query_runner.mysql",
"redash.query_runner.pg",
"redash.query_runner.url",
"redash.query_runner.influx_db",
"redash.query_runner.elasticsearch",
"redash.query_runner.amazon_elasticsearch",
"redash.query_runner.trino",
"redash.query_runner.presto",
"redash.query_runner.databricks",
"redash.query_runner.hive_ds",
"redash.query_runner.impala_ds",
"redash.query_runner.vertica",
"redash.query_runner.clickhouse",
"redash.query_runner.yandex_metrica",
"redash.query_runner.rockset",
"redash.query_runner.treasuredata",
"redash.query_runner.sqlite",
"redash.query_runner.dynamodb_sql",
"redash.query_runner.mssql",
"redash.query_runner.mssql_odbc",
"redash.query_runner.memsql_ds",
"redash.query_runner.mapd",
"redash.query_runner.jql",
"redash.query_runner.google_analytics",
"redash.query_runner.axibase_tsd",
"redash.query_runner.salesforce",
"redash.query_runner.query_results",
"redash.query_runner.prometheus",
"redash.query_runner.qubole",
"redash.query_runner.db2",
"redash.query_runner.druid",
"redash.query_runner.kylin",
"redash.query_runner.drill",
"redash.query_runner.uptycs",
"redash.query_runner.snowflake",
"redash.query_runner.phoenix",
"redash.query_runner.json_ds",
"redash.query_runner.cass",
"redash.query_runner.dgraph",
"redash.query_runner.azure_kusto",
"redash.query_runner.exasol",
"redash.query_runner.cloudwatch",
"redash.query_runner.cloudwatch_insights",
"redash.query_runner.corporate_memory",
"redash.query_runner.sparql_endpoint",
"redash.query_runner.excel",
"redash.query_runner.csv",
"redash.query_runner.firebolt"
]
enabled_query_runners = array_from_string(
os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join(default_query_runners))
)
additional_query_runners = array_from_string(
os.environ.get("REDASH_ADDITIONAL_QUERY_RUNNERS", "")
)
disabled_query_runners = array_from_string(
os.environ.get("REDASH_DISABLED_QUERY_RUNNERS", "")
)
QUERY_RUNNERS = remove(
set(disabled_query_runners),
distinct(enabled_query_runners + additional_query_runners),
)
dynamic_settings = importlib.import_module(
os.environ.get("REDASH_DYNAMIC_SETTINGS_MODULE", "redash.settings.dynamic_settings")
)
# Destinations
default_destinations = [
"redash.destinations.email",
"redash.destinations.slack",
"redash.destinations.webhook",
"redash.destinations.hipchat",
"redash.destinations.mattermost",
"redash.destinations.chatwork",
"redash.destinations.pagerduty",
"redash.destinations.hangoutschat",
]
enabled_destinations = array_from_string(
os.environ.get("REDASH_ENABLED_DESTINATIONS", ",".join(default_destinations))
)
additional_destinations = array_from_string(
os.environ.get("REDASH_ADDITIONAL_DESTINATIONS", "")
)
DESTINATIONS = distinct(enabled_destinations + additional_destinations)
EVENT_REPORTING_WEBHOOKS = array_from_string(
os.environ.get("REDASH_EVENT_REPORTING_WEBHOOKS", "")
)
# Support for Sentry (https://getsentry.com/). Just set your Sentry DSN to enable it:
SENTRY_DSN = os.environ.get("REDASH_SENTRY_DSN", "")
SENTRY_ENVIRONMENT = os.environ.get("REDASH_SENTRY_ENVIRONMENT")
# Client side toggles:
ALLOW_SCRIPTS_IN_USER_INPUT = parse_boolean(
os.environ.get("REDASH_ALLOW_SCRIPTS_IN_USER_INPUT", "false")
)
DASHBOARD_REFRESH_INTERVALS = list(
map(
int,
array_from_string(
os.environ.get(
"REDASH_DASHBOARD_REFRESH_INTERVALS", "60,300,600,1800,3600,43200,86400"
)
),
)
)
QUERY_REFRESH_INTERVALS = list(
map(
int,
array_from_string(
os.environ.get(
"REDASH_QUERY_REFRESH_INTERVALS",
"60, 300, 600, 900, 1800, 3600, 7200, 10800, 14400, 18000, 21600, 25200, 28800, 32400, 36000, 39600, 43200, 86400, 604800, 1209600, 2592000",
)
),
)
)
PAGE_SIZE = int(os.environ.get("REDASH_PAGE_SIZE", 20))
PAGE_SIZE_OPTIONS = list(
map(
int,
array_from_string(os.environ.get("REDASH_PAGE_SIZE_OPTIONS", "5,10,20,50,100")),
)
)
TABLE_CELL_MAX_JSON_SIZE = int(os.environ.get("REDASH_TABLE_CELL_MAX_JSON_SIZE", 50000))
# Features:
VERSION_CHECK = parse_boolean(os.environ.get("REDASH_VERSION_CHECK", "true"))
FEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(
os.environ.get("REDASH_FEATURE_DISABLE_REFRESH_QUERIES", "false")
)
FEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(
os.environ.get("REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT", "true")
)
FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS = parse_boolean(
os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", "false")
)
FEATURE_AUTO_PUBLISH_NAMED_QUERIES = parse_boolean(
os.environ.get("REDASH_FEATURE_AUTO_PUBLISH_NAMED_QUERIES", "true")
)
FEATURE_EXTENDED_ALERT_OPTIONS = parse_boolean(
os.environ.get("REDASH_FEATURE_EXTENDED_ALERT_OPTIONS", "false")
)
# BigQuery
BIGQUERY_HTTP_TIMEOUT = int(os.environ.get("REDASH_BIGQUERY_HTTP_TIMEOUT", "600"))
# Allow Parameters in Embeds
# WARNING: Deprecated!
# See https://discuss.redash.io/t/support-for-parameters-in-embedded-visualizations/3337 for more details.
ALLOW_PARAMETERS_IN_EMBEDS = parse_boolean(
os.environ.get("REDASH_ALLOW_PARAMETERS_IN_EMBEDS", "false")
)
# Enhance schema fetching
SCHEMA_RUN_TABLE_SIZE_CALCULATIONS = parse_boolean(
os.environ.get("REDASH_SCHEMA_RUN_TABLE_SIZE_CALCULATIONS", "false")
)
# kylin
KYLIN_OFFSET = int(os.environ.get("REDASH_KYLIN_OFFSET", 0))
KYLIN_LIMIT = int(os.environ.get("REDASH_KYLIN_LIMIT", 50000))
KYLIN_ACCEPT_PARTIAL = parse_boolean(
os.environ.get("REDASH_KYLIN_ACCEPT_PARTIAL", "false")
)
# sqlparse
SQLPARSE_FORMAT_OPTIONS = {
"reindent": parse_boolean(os.environ.get("SQLPARSE_FORMAT_REINDENT", "true")),
"keyword_case": os.environ.get("SQLPARSE_FORMAT_KEYWORD_CASE", "upper"),
}
# requests
REQUESTS_ALLOW_REDIRECTS = parse_boolean(
os.environ.get("REDASH_REQUESTS_ALLOW_REDIRECTS", "false")
)
# Enforces CSRF token validation on API requests.
# This is turned off by default to avoid breaking any existing deployments but it is highly recommended to turn this toggle on to prevent CSRF attacks.
ENFORCE_CSRF = parse_boolean(
os.environ.get("REDASH_ENFORCE_CSRF", "false")
)
# Databricks
CSRF_TIME_LIMIT = int(os.environ.get("REDASH_CSRF_TIME_LIMIT", 3600 * 6))
# Email blocked domains, use delimiter comma to separated multiple domains
BLOCKED_DOMAINS = set_from_string(os.environ.get("REDASH_BLOCKED_DOMAINS", "qq.com"))