diff --git a/enterprise/backend/src/metabase_enterprise/sso/api/interface.clj b/enterprise/backend/src/metabase_enterprise/sso/api/interface.clj new file mode 100644 index 0000000000000000000000000000000000000000..76e70a5eccfa81ed7d126aeb39e890a26f2fb46d --- /dev/null +++ b/enterprise/backend/src/metabase_enterprise/sso/api/interface.clj @@ -0,0 +1,34 @@ +(ns metabase-enterprise.sso.api.interface + (:require [metabase-enterprise.sso.integrations.sso-settings :as sso-settings] + [metabase.util.i18n :refer [tru]])) + +(defn- sso-backend + "Function that powers the defmulti in figuring out which SSO backend to use. It might be that we need to have more + complex logic around this, but now it's just a simple priority. If SAML is configured use that otherwise JWT" + [_] + (cond + (sso-settings/saml-configured?) :saml + (sso-settings/jwt-enabled) :jwt + :else nil)) + +(defmulti sso-get + "Multi-method for supporting the first part of an SSO signin request. An implementation of this method will usually + result in a redirect to an SSO backend" + sso-backend) + +(defmulti sso-post + "Multi-method for supporting a POST-back from an SSO signin request. An implementation of this method will need to + validate the POST from the SSO backend and successfully log the user into Metabase." + sso-backend) + +(defn- throw-not-configured-error [] + (throw (ex-info (str (tru "SSO has not been enabled and/or configured")) + {:status-code 400}))) + +(defmethod sso-get :default + [_] + (throw-not-configured-error)) + +(defmethod sso-post :default + [_] + (throw-not-configured-error)) diff --git a/enterprise/backend/src/metabase_enterprise/sso/api/sso.clj b/enterprise/backend/src/metabase_enterprise/sso/api/sso.clj index 830dedc21f4de64c0db62ee74f7d9a4eebde0542..39f7cad3f01199c61215a565e93f3393630963f1 100644 --- a/enterprise/backend/src/metabase_enterprise/sso/api/sso.clj +++ b/enterprise/backend/src/metabase_enterprise/sso/api/sso.clj @@ -5,48 +5,18 @@ we can have a uniform interface both via the API and code" (:require [clojure.tools.logging :as log] [compojure.core :refer [GET POST]] - [metabase-enterprise.sso.integrations.sso-settings :as sso-settings] + [metabase-enterprise.sso.api.interface :as sso.i] + metabase-enterprise.sso.integrations.jwt + metabase-enterprise.sso.integrations.saml [metabase.api.common :as api] - [metabase.plugins.classloader :as classloader] [metabase.public-settings.premium-features :as premium-features] [metabase.util :as u] [metabase.util.i18n :refer [trs tru]] [stencil.core :as stencil])) -(defn- sso-backend - "Function that powers the defmulti in figuring out which SSO backend to use. It might be that we need to have more - complex logic around this, but now it's just a simple priority. If SAML is configured use that otherwise JWT" - [_] - ;; load the SSO integrations so their implementations for the multimethods below are available. Can't load in - ;; `:require` because it would cause a circular ref / those namespaces aren't used here at any rate - ;; (`cljr-clean-namespace` would remove them) - (classloader/require '[metabase-enterprise.sso.integrations jwt saml]) - (cond - (sso-settings/saml-configured?) :saml - (sso-settings/jwt-enabled) :jwt - :else nil)) - -(defmulti sso-get - "Multi-method for supporting the first part of an SSO signin request. An implementation of this method will usually - result in a redirect to an SSO backend" - sso-backend) - -(defmulti sso-post - "Multi-method for supporting a POST-back from an SSO signin request. An implementation of this method will need to - validate the POST from the SSO backend and successfully log the user into Metabase." - sso-backend) - -(defn- throw-not-configured-error [] - (throw (ex-info (str (tru "SSO has not been enabled and/or configured")) - {:status-code 400}))) - -(defmethod sso-get :default - [_] - (throw-not-configured-error)) - -(defmethod sso-post :default - [_] - (throw-not-configured-error)) +;; 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) (defn- throw-if-no-premium-features-token [] (when-not (premium-features/enable-sso?) @@ -58,7 +28,7 @@ {:as req} (throw-if-no-premium-features-token) (try - (sso-get req) + (sso.i/sso-get req) (catch Throwable e (log/error #_e (trs "Error returning SSO entry point")) (throw e)))) @@ -78,7 +48,7 @@ {:as req} (throw-if-no-premium-features-token) (try - (sso-post req) + (sso.i/sso-post req) (catch Throwable e (log/error e (trs "Error logging in")) (sso-error-page e)))) diff --git a/enterprise/backend/src/metabase_enterprise/sso/integrations/jwt.clj b/enterprise/backend/src/metabase_enterprise/sso/integrations/jwt.clj index cd5d07e1f76c1fb1ac27424fce2588bf01893fcc..0b5848cf57a89c042f3b9ffd4c55463bfb60f1de 100644 --- a/enterprise/backend/src/metabase_enterprise/sso/integrations/jwt.clj +++ b/enterprise/backend/src/metabase_enterprise/sso/integrations/jwt.clj @@ -1,7 +1,7 @@ (ns metabase-enterprise.sso.integrations.jwt "Implementation of the JWT backend for sso" (:require [buddy.sign.jwt :as jwt] - [metabase-enterprise.sso.api.sso :as sso] + [metabase-enterprise.sso.api.interface :as sso.i] [metabase-enterprise.sso.integrations.sso-settings :as sso-settings] [metabase-enterprise.sso.integrations.sso-utils :as sso-utils] [metabase.api.common :as api] @@ -90,7 +90,7 @@ (api/check (sso-settings/jwt-configured?) [400 (tru "JWT SSO has not been enabled and/or configured")])) -(defmethod sso/sso-get :jwt +(defmethod sso.i/sso-get :jwt [{{:keys [jwt redirect]} :params, :as request}] (check-jwt-enabled) (if jwt @@ -99,6 +99,6 @@ (when redirect (str "?return_to=" redirect)))))) -(defmethod sso/sso-post :jwt +(defmethod sso.i/sso-post :jwt [req] (throw (ex-info "POST not valid for JWT SSO requests" {:status-code 400}))) diff --git a/enterprise/backend/src/metabase_enterprise/sso/integrations/saml.clj b/enterprise/backend/src/metabase_enterprise/sso/integrations/saml.clj index d9fbd0935c243d965a61414a3e8c7bb477afc7d6..89d22dc27f52605e3ae497fac9da4408d00834b2 100644 --- a/enterprise/backend/src/metabase_enterprise/sso/integrations/saml.clj +++ b/enterprise/backend/src/metabase_enterprise/sso/integrations/saml.clj @@ -20,7 +20,7 @@ [clojure.string :as str] [clojure.tools.logging :as log] [medley.core :as m] - [metabase-enterprise.sso.api.sso :as sso] + [metabase-enterprise.sso.api.interface :as sso.i] [metabase-enterprise.sso.integrations.sso-settings :as sso-settings] [metabase-enterprise.sso.integrations.sso-utils :as sso-utils] [metabase.api.common :as api] @@ -107,7 +107,7 @@ (api/check (sso-settings/saml-configured?) [400 (tru "SAML has not been enabled and/or configured")])) -(defmethod sso/sso-get :saml +(defmethod sso.i/sso-get :saml ;; Initial call that will result in a redirect to the IDP along with information about how the IDP can authenticate ;; and redirect them back to us [req] @@ -170,7 +170,7 @@ (when (u/base64-string? s) (codecs/bytes->str (codec/base64-decode s)))) -(defmethod sso/sso-post :saml +(defmethod sso.i/sso-post :saml ;; Does the verification of the IDP's response and 'logs the user in'. The attributes are available in the response: ;; `(get-in saml-info [:assertions :attrs]) [{:keys [params], :as request}] diff --git a/src/metabase/api/common.clj b/src/metabase/api/common.clj index edc2987028d96ce2db8198eb4a67dffe4eec59a9..ff10d7c87581dcb40a68a9356a377daf7c467294 100644 --- a/src/metabase/api/common.clj +++ b/src/metabase/api/common.clj @@ -248,7 +248,6 @@ "Impl macro for `defendpoint`; don't use this directly." [{:keys [method route fn-name docstr args arg->schema original-body body]}] {:pre [(or (string? route) (vector? route))]} - (require 'compojure.core) `(def ~(vary-meta fn-name assoc diff --git a/src/metabase/core.clj b/src/metabase/core.clj index 9b04eef9a1f6a9804c3198a0f1824121249954ea..5e48d3a23bb48d3785f27ee309d55d2269a88d02 100644 --- a/src/metabase/core.clj +++ b/src/metabase/core.clj @@ -6,6 +6,9 @@ [metabase.config :as config] [metabase.core.initialization-status :as init-status] [metabase.db :as mdb] + metabase.driver.h2 + metabase.driver.mysql + metabase.driver.postgres [metabase.events :as events] [metabase.metabot :as metabot] [metabase.models.user :refer [User]] @@ -21,6 +24,11 @@ [metabase.util.i18n :refer [deferred-trs trs]] [toucan.db :as db])) + ;; Load up the drivers shipped as part of the main codebase, so they will show up in the list of available DB types +(comment metabase.driver.h2/keep-me + metabase.driver.mysql/keep-me + metabase.driver.postgres/keep-me) + ;; don't i18n this, it's legalese (log/info (format "\nMetabase %s" config/mb-version-string) @@ -78,10 +86,6 @@ (plugins/load-plugins!) (init-status/set-progress! 0.3) - ;; Load up the drivers shipped as part of the main codebase, so they will show up in the list of available DB types - (classloader/require 'metabase.driver.h2 'metabase.driver.postgres 'metabase.driver.mysql) - (init-status/set-progress! 0.4) - ;; startup database. validates connection & runs any necessary migrations (log/info (trs "Setting up and migrating Metabase DB. Please sit tight, this may take a minute...")) (mdb/setup-db!) diff --git a/src/metabase/util/honeysql_extensions.clj b/src/metabase/util/honeysql_extensions.clj index aa940041c52b515dd83412e076626495c0f77791..ba1e4da7b88f57e2504e20d2a34ab9cda916f08a 100644 --- a/src/metabase/util/honeysql_extensions.clj +++ b/src/metabase/util/honeysql_extensions.clj @@ -4,6 +4,7 @@ [clojure.string :as str] [honeysql.core :as hsql] [honeysql.format :as hformat] + honeysql.types [metabase.util :as u] [metabase.util.schema :as su] [potemkin.types :as p.types] @@ -12,6 +13,8 @@ (:import honeysql.format.ToSql java.util.Locale)) +(comment honeysql.types/keep-me) + (defn- english-upper-case "Use this function when you need to upper-case an identifier or table name. Similar to `clojure.string/upper-case` but always converts the string to upper-case characters in the English locale. Using `clojure.string/upper-case` for @@ -287,7 +290,6 @@ (alter-meta! #'honeysql.core/format assoc :style/indent :defn) (alter-meta! #'honeysql.core/call assoc :style/indent :defn) -(require 'honeysql.types) (extend-protocol PrettyPrintable honeysql.types.SqlCall (pretty [{fn-name :name, args :args, :as this}]