Middleware related to enforcing authentication/API keys (when applicable). Unlike most other middleware most of this is not used as part of the normal app; it is instead added selectively to appropriate routes.

(ns metabase.server.middleware.auth
  (:require
   [clojure.string :as str]
   [metabase.models.setting :refer [defsetting]]
   [metabase.request.core :as request]
   [metabase.util.i18n :refer [deferred-trs]]))
(def ^:private ^:const ^String static-metabase-api-key-header "x-metabase-apikey")

Middleware that returns a 401 response if request has no associated :metabase-user-id.

(defn enforce-authentication
  [handler]
  (with-meta
   (fn [{:keys [metabase-user-id] :as request} respond raise]
     (if metabase-user-id
       (handler request respond raise)
       (respond request/response-unauthentic)))
   (meta handler)))
(defn- wrap-static-api-key* [{:keys [headers], :as request}]
  (if-let [api-key (headers static-metabase-api-key-header)]
    (assoc request :static-metabase-api-key api-key)
    request))

Middleware that sets the :static-metabase-api-key keyword on the request if a valid API Key can be found. We check the request headers for X-METABASE-APIKEY and if it's not found then no keyword is bound to the request.

(defn wrap-static-api-key
  [handler]
  (with-meta
   (fn [request respond raise]
     (handler (wrap-static-api-key* request) respond raise))
   (meta handler)))

When set, this API key is required for all API requests.

(defsetting api-key
  :encryption :when-encryption-key-set
  :visibility :internal
  :doc "Middleware that enforces validation of the client via the request header X-Metabase-Apikey.
        If the header is available, then it’s validated against MB_API_KEY.
        When it matches, the request continues; otherwise it’s blocked with a 403 Forbidden response.")

We don't want to change the name of the setting from MB_API_KEY, but we want to differentiate this static key from the API keys that can be generated by admins.

(defn static-api-key
  [] (api-key))

Url for documentation on how to set MBAPIKEY.

(def mb-api-key-doc-url
  "https://www.metabase.com/docs/latest/configuring-metabase/environment-variables#mb_api_key")

Response when the MBAPIKEY is not set.

(def key-not-set-response
  {:status 403
   :body (deferred-trs "MB_API_KEY is not set. See {0} for details" mb-api-key-doc-url)})

Middleware that enforces validation of the client via API Key, canceling the request processing if the check fails.

Validation is handled by first checking for the presence of the :static-metabase-api-key on the request. If the api key is available then we validate it by checking it against the configured :mb-api-key value set in our global config.

If the request :static-metabase-api-key matches the configured api-key value then the request continues, otherwise we reject the request and return a 403 Forbidden response.

This variable only works for /api/notify/db/:id endpoint

(defn enforce-static-api-key
  [handler]
  (with-meta
   (fn [{:keys [static-metabase-api-key], :as request} respond raise]
     (cond (str/blank? (static-api-key))
           (respond key-not-set-response)
           (not static-metabase-api-key)
           (respond request/response-forbidden)
           (= (static-api-key) static-metabase-api-key)
           (handler request respond raise)
           :else
           (respond request/response-forbidden)))
   (meta handler)))