Implements the SSO routes needed for SAML and JWT. This namespace primarily provides hooks for those two backends so we can have a uniform interface both via the API and code | (ns metabase-enterprise.sso.api.sso (:require [metabase-enterprise.sso.api.interface :as sso.i] [metabase-enterprise.sso.integrations.jwt] [metabase-enterprise.sso.integrations.saml] [metabase-enterprise.sso.integrations.sso-settings :as sso-settings] [metabase.api.common :as api] [metabase.api.macros :as api.macros] [metabase.request.core :as request] [metabase.util :as u] [metabase.util.log :as log] [metabase.util.malli :as mu] [metabase.util.urls :as urls] [saml20-clj.core :as saml] [saml20-clj.encode-decode :as encode-decode] [stencil.core :as stencil] [toucan2.core :as t2])) |
(set! *warn-on-reflection* true) | |
load the SSO integrations so their implementations for the multimethods below are available. | (comment metabase-enterprise.sso.integrations.jwt/keep-me metabase-enterprise.sso.integrations.saml/keep-me) |
GET /auth/sso | (api.macros/defendpoint :get "/" "SSO entry-point for an SSO user that has not logged in yet" [_route-params _query-params _body request] (try (sso.i/sso-get request) (catch Throwable e (log/error #_e "Error returning SSO entry point") (throw e)))) |
(mu/defn- sso-error-page [^Throwable e log-direction :- [:enum :in :out]] {:status (get (ex-data e) :status-code 500) :headers {"Content-Type" "text/html"} :body (stencil/render-file "metabase_enterprise/sandbox/api/error_page" (let [message (.getMessage e) data (u/pprint-to-str (ex-data e))] {:logDirection (name log-direction) :errorMessage message :exceptionClass (.getName Exception) :additionalData data}))}) | |
POST /auth/sso | (api.macros/defendpoint :post "/" "Route the SSO backends call with successful login details" [_route-params _query-params _body request] (try (sso.i/sso-post request) (catch Throwable e (log/error e "Error logging in") (sso-error-page e :in)))) |
------------------------------ Single Logout aka SLO ------------------------------ | |
The url that the IdP should respond to. Not all IdPs support this, but it's a good idea to send it just in case. | (def metabase-slo-redirect-url "/auth/sso/handle_slo") |
POST /auth/sso/logout | (api.macros/defendpoint :post "/logout" "Logout." [_route-params _query-params _body {cookies :cookies, :as _request}] (let [metabase-session-id (get-in cookies [request/metabase-session-cookie :value])] (api/check-exists? :model/Session metabase-session-id) (let [{:keys [email sso_source]} (t2/query-one {:select [:u.email :u.sso_source] :from [[:core_user :u]] :join [[:core_session :session] [:= :u.id :session.user_id]] :where [:= :session.id metabase-session-id]})] ;; If a user doesn't have SLO setup on their IdP, ;; they will never hit "/handle_slo" so we must delete the session here: (t2/delete! :model/Session :id metabase-session-id) {:saml-logout-url (when (and (sso-settings/saml-slo-enabled) (= sso_source "saml")) (saml/logout-redirect-location :idp-url (sso-settings/saml-identity-provider-uri) :issuer (sso-settings/saml-application-name) :user-email email :relay-state (encode-decode/str->base64 (str (urls/site-url) metabase-slo-redirect-url))))}))) |
POST /auth/sso/handle_slo | (api.macros/defendpoint :post "/handle_slo" "Handles client confirmation of saml logout via slo" [_route-params _query-params _body request] (try (if (sso-settings/saml-slo-enabled) (sso.i/sso-handle-slo request) (throw (ex-info "SAML Single Logout is not enabled, request forbidden." {:status-code 403}))) (catch Throwable e (log/error e "Error handling SLO") (sso-error-page e :out)))) |