mirror of
https://github.com/getredash/redash.git
synced 2025-12-20 09:57:35 -05:00
Compare commits
15 Commits
query-base
...
saml-setti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
253468d050 | ||
|
|
6e78bddf4c | ||
|
|
6339665a34 | ||
|
|
420f444614 | ||
|
|
e1a5418364 | ||
|
|
f694f97165 | ||
|
|
219d914a24 | ||
|
|
7553690922 | ||
|
|
340980e153 | ||
|
|
15208cce66 | ||
|
|
9556560cb4 | ||
|
|
0ad99bd714 | ||
|
|
8a2f90ffe8 | ||
|
|
b1040a5f9a | ||
|
|
6ac181666b |
@@ -1,45 +1,80 @@
|
||||
import React from "react";
|
||||
import Form from "antd/lib/form";
|
||||
import Checkbox from "antd/lib/checkbox";
|
||||
import Input from "antd/lib/input";
|
||||
import Radio from "antd/lib/radio";
|
||||
import DynamicComponent from "@/components/DynamicComponent";
|
||||
import { SettingsEditorPropTypes, SettingsEditorDefaultProps } from "../prop-types";
|
||||
|
||||
export default function SAMLSettings(props) {
|
||||
const { values, onChange } = props;
|
||||
|
||||
const onChangeEnabledStatus = e => {
|
||||
const updates = { auth_saml_enabled: !!e.target.value };
|
||||
if (e.target.value) {
|
||||
updates.auth_saml_type = e.target.value;
|
||||
}
|
||||
onChange(updates);
|
||||
};
|
||||
|
||||
return (
|
||||
<DynamicComponent name="OrganizationSettings.SAMLSettings" {...props}>
|
||||
<h4>SAML</h4>
|
||||
<Form.Item label="SAML Enabled">
|
||||
<Checkbox
|
||||
name="auth_saml_enabled"
|
||||
checked={values.auth_saml_enabled}
|
||||
onChange={e => onChange({ auth_saml_enabled: e.target.checked })}>
|
||||
SAML Enabled
|
||||
</Checkbox>
|
||||
<Radio.Group
|
||||
onChange={onChangeEnabledStatus}
|
||||
value={values.auth_saml_enabled && (values.auth_saml_type || "static")}>
|
||||
<Radio value={false}>Disabled</Radio>
|
||||
<Radio value={"static"}>Enabled (Static)</Radio>
|
||||
<Radio value={"dynamic"}>Enabled (Dynamic)</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
{values.auth_saml_enabled && (
|
||||
<div>
|
||||
<Form.Item label="SAML Metadata URL">
|
||||
<Input
|
||||
value={values.auth_saml_metadata_url}
|
||||
onChange={e => onChange({ auth_saml_metadata_url: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="SAML Entity ID">
|
||||
<Input
|
||||
value={values.auth_saml_entity_id}
|
||||
onChange={e => onChange({ auth_saml_entity_id: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="SAML NameID Format">
|
||||
<Input
|
||||
value={values.auth_saml_nameid_format}
|
||||
onChange={e => onChange({ auth_saml_nameid_format: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<>
|
||||
{values.auth_saml_type === "static" && (
|
||||
<>
|
||||
<Form.Item label="SAML Single Sign-on URL">
|
||||
<Input
|
||||
value={values.auth_saml_sso_url}
|
||||
onChange={e => onChange({ auth_saml_sso_url: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="SAML Entity ID">
|
||||
<Input
|
||||
value={values.auth_saml_entity_id}
|
||||
onChange={e => onChange({ auth_saml_entity_id: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="SAML x509 cert">
|
||||
<Input
|
||||
value={values.auth_saml_x509_cert}
|
||||
onChange={e => onChange({ auth_saml_x509_cert: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
{values.auth_saml_type === "dynamic" && (
|
||||
<>
|
||||
<Form.Item label="SAML Metadata URL">
|
||||
<Input
|
||||
value={values.auth_saml_metadata_url}
|
||||
onChange={e => onChange({ auth_saml_metadata_url: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="SAML Entity ID">
|
||||
<Input
|
||||
value={values.auth_saml_entity_id}
|
||||
onChange={e => onChange({ auth_saml_entity_id: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="SAML NameID Format">
|
||||
<Input
|
||||
value={values.auth_saml_nameid_format}
|
||||
onChange={e => onChange({ auth_saml_nameid_format: e.target.value })}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DynamicComponent>
|
||||
);
|
||||
|
||||
@@ -4,13 +4,17 @@ from redash import settings
|
||||
from redash.authentication import create_and_login_user, logout_and_redirect_to_index
|
||||
from redash.authentication.org_resolving import current_org
|
||||
from redash.handlers.base import org_scoped_rule
|
||||
from redash.utils import mustache_render
|
||||
from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT, entity
|
||||
from saml2.client import Saml2Client
|
||||
from saml2.config import Config as Saml2Config
|
||||
from saml2.saml import NAMEID_FORMAT_TRANSIENT
|
||||
from saml2.sigver import get_xmlsec_binary
|
||||
|
||||
|
||||
logger = logging.getLogger("saml_auth")
|
||||
blueprint = Blueprint("saml_auth", __name__)
|
||||
inline_metadata_template = """<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor entityID="{{entity_id}}" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"><md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>{{x509_cert}}</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="{{sso_url}}"/><md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="{{sso_url}}"/></md:IDPSSODescriptor></md:EntityDescriptor>"""
|
||||
|
||||
|
||||
def get_saml_client(org):
|
||||
@@ -19,12 +23,20 @@ def get_saml_client(org):
|
||||
|
||||
The configuration is a hash for use by saml2.config.Config
|
||||
"""
|
||||
metadata_url = org.get_setting("auth_saml_metadata_url")
|
||||
|
||||
saml_type = org.get_setting("auth_saml_type")
|
||||
entity_id = org.get_setting("auth_saml_entity_id")
|
||||
sso_url = org.get_setting("auth_saml_sso_url")
|
||||
x509_cert = org.get_setting("auth_saml_x509_cert")
|
||||
metadata_url = org.get_setting("auth_saml_metadata_url")
|
||||
|
||||
if settings.SAML_SCHEME_OVERRIDE:
|
||||
acs_url = url_for("saml_auth.idp_initiated", org_slug=org.slug, _external=True,
|
||||
_scheme=settings.SAML_SCHEME_OVERRIDE)
|
||||
acs_url = url_for(
|
||||
"saml_auth.idp_initiated",
|
||||
org_slug=org.slug,
|
||||
_external=True,
|
||||
_scheme=settings.SAML_SCHEME_OVERRIDE,
|
||||
)
|
||||
else:
|
||||
acs_url = url_for("saml_auth.idp_initiated", org_slug=org.slug, _external=True)
|
||||
|
||||
@@ -51,8 +63,30 @@ def get_saml_client(org):
|
||||
},
|
||||
}
|
||||
|
||||
if entity_id is not None and entity_id != "":
|
||||
saml_settings["entityid"] = entity_id
|
||||
if settings.SAML_ENCRYPTION_ENABLED:
|
||||
encryption_dict = {
|
||||
"xmlsec_binary": get_xmlsec_binary(),
|
||||
"encryption_keypairs": [
|
||||
{
|
||||
"key_file": settings.SAML_ENCRYPTION_PEM_PATH,
|
||||
"cert_file": settings.SAML_ENCRYPTION_CERT_PATH,
|
||||
}
|
||||
],
|
||||
}
|
||||
saml_settings.update(encryption_dict)
|
||||
|
||||
if saml_type is not None and saml_type == "static":
|
||||
metadata_inline = mustache_render(
|
||||
inline_metadata_template,
|
||||
entity_id=entity_id,
|
||||
x509_cert=x509_cert,
|
||||
sso_url=sso_url,
|
||||
)
|
||||
|
||||
saml_settings["metadata"] = {"inline": [metadata_inline]}
|
||||
|
||||
if acs_url is not None and acs_url != "":
|
||||
saml_settings["entityid"] = acs_url
|
||||
|
||||
sp_config = Saml2Config()
|
||||
sp_config.load(saml_settings)
|
||||
|
||||
@@ -167,6 +167,10 @@ GOOGLE_OAUTH_ENABLED = bool(GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET)
|
||||
# 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.
|
||||
#
|
||||
|
||||
@@ -14,10 +14,13 @@ PASSWORD_LOGIN_ENABLED = parse_boolean(
|
||||
os.environ.get("REDASH_PASSWORD_LOGIN_ENABLED", "true")
|
||||
)
|
||||
|
||||
SAML_LOGIN_TYPE = os.environ.get("REDASH_SAML_AUTH_TYPE", "")
|
||||
SAML_METADATA_URL = os.environ.get("REDASH_SAML_METADATA_URL", "")
|
||||
SAML_ENTITY_ID = os.environ.get("REDASH_SAML_ENTITY_ID", "")
|
||||
SAML_NAMEID_FORMAT = os.environ.get("REDASH_SAML_NAMEID_FORMAT", "")
|
||||
SAML_LOGIN_ENABLED = SAML_METADATA_URL != ""
|
||||
SAML_SSO_URL = os.environ.get("REDASH_SAML_SSO_URL", "")
|
||||
SAML_X509_CERT = os.environ.get("REDASH_SAML_X509_CERT", "")
|
||||
SAML_LOGIN_ENABLED = SAML_SSO_URL != "" and SAML_METADATA_URL != ""
|
||||
|
||||
DATE_FORMAT = os.environ.get("REDASH_DATE_FORMAT", "DD/MM/YY")
|
||||
TIME_FORMAT = os.environ.get("REDASH_TIME_FORMAT", "HH:mm")
|
||||
@@ -52,9 +55,12 @@ settings = {
|
||||
"beacon_consent": None,
|
||||
"auth_password_login_enabled": PASSWORD_LOGIN_ENABLED,
|
||||
"auth_saml_enabled": SAML_LOGIN_ENABLED,
|
||||
"auth_saml_type": SAML_LOGIN_TYPE,
|
||||
"auth_saml_entity_id": SAML_ENTITY_ID,
|
||||
"auth_saml_metadata_url": SAML_METADATA_URL,
|
||||
"auth_saml_nameid_format": SAML_NAMEID_FORMAT,
|
||||
"auth_saml_sso_url": SAML_SSO_URL,
|
||||
"auth_saml_x509_cert": SAML_X509_CERT,
|
||||
"date_format": DATE_FORMAT,
|
||||
"time_format": TIME_FORMAT,
|
||||
"integer_format": INTEGER_FORMAT,
|
||||
|
||||
@@ -39,7 +39,7 @@ rq==1.1.0
|
||||
rq-scheduler==0.9.1
|
||||
jsonschema==3.1.1
|
||||
RestrictedPython==5.0
|
||||
pysaml2==4.8.0
|
||||
pysaml2==6.1.0
|
||||
pycrypto==2.6.1
|
||||
funcy==1.13
|
||||
sentry-sdk>=0.14.3,<0.15.0
|
||||
|
||||
Reference in New Issue
Block a user