Utility functions for public links and embedding. | (ns metabase.util.embed (:require [buddy.core.codecs :as codecs] [buddy.sign.jwt :as jwt] [clojure.string :as str] [hiccup.core :refer [html]] [metabase.config :as config] [metabase.models.setting :as setting :refer [defsetting]] [metabase.public-settings :as public-settings] [metabase.public-settings.premium-features :as premium-features] [metabase.util :as u] [metabase.util.i18n :refer [deferred-tru trs tru]] [metabase.util.json :as json] [ring.util.codec :as codec])) |
(set! *warn-on-reflection* true) | |
--------------------------------------------- PUBLIC LINKS UTIL FNS ---------------------------------------------- | |
Return an oEmbed URL for the (oembed-url "/x") -> "http://localhost:3000/api/public/oembed?url=x&format=json" | (defn- oembed-url ^String [^String relative-url] (str (public-settings/site-url) "/api/public/oembed" ;; NOTE: some oEmbed consumers require `url` be the first param??? "?url=" (codec/url-encode (str (public-settings/site-url) relative-url)) "&format=json")) |
Returns a | (defn- oembed-link ^String [^String url] (html [:link {:rel "alternate" :type "application/json+oembed" :href (oembed-url url) :title "Metabase"}])) |
A | (def ^:private ^:const ^String embedly-meta (html [:meta {:name "generator", :content "Metabase"}])) |
Returns the | (defn head ^String [^String url] (str embedly-meta (oembed-link url))) |
Return an | (defn iframe ^String [^String url, width height] (html [:iframe {:src url :width width :height height :frameborder 0}])) |
----------------------------------------------- EMBEDDING UTIL FNS ----------------------------------------------- | |
(defsetting embedding-secret-key (deferred-tru "Secret key used to sign JSON Web Tokens for requests to `/api/embed` endpoints.") :encryption :when-encryption-key-set :visibility :admin :audit :no-value :setter (fn [new-value] (when (seq new-value) (assert (u/hexadecimal-string? new-value) (tru "Invalid embedding-secret-key! Secret key must be a hexadecimal-encoded 256-bit key (i.e., a 64-character string)."))) (setting/set-value-of-type! :string :embedding-secret-key new-value))) | |
Parse a JWT | (defn- jwt-header [^String message] (let [[header] (str/split message #"\.")] (json/decode+kw (codecs/bytes->str (codec/base64-decode header))))) |
Check that the JWT | (defn- check-valid-alg [^String message] (let [{:keys [alg]} (jwt-header message)] (when-not alg (throw (Exception. (trs "JWT is missing `alg`.")))) (when (= alg "none") (throw (Exception. (trs "JWT `alg` cannot be `none`.")))))) |
Parse a "signed" (base-64 encoded) JWT and return a Clojure representation. Check that the signature is
valid (i.e., check that it was signed with | (defn unsign [^String message] (when (seq message) (try (check-valid-alg message) (jwt/unsign message (or (embedding-secret-key) (throw (ex-info (tru "The embedding secret key has not been set.") {:status-code 400}))) ;; The library will reject tokens with a created at timestamp in the future, so to account for clock ;; skew tell the library to allow for 60 seconds of leeway {:leeway 60}) ;; if `jwt/unsign` throws an Exception rethrow it in a format that's friendlier to our API (catch Throwable e (throw (ex-info (.getMessage e) {:status-code 400})))))) |
Find | (defn get-in-unsigned-token-or-throw [unsigned-token keyseq] (or (get-in unsigned-token keyseq) (throw (ex-info (tru "Token is missing value for keypath {0}" keyseq) {:status-code 400})))) |
Populate | (defn maybe-populate-initially-published-at [{:keys [enable_embedding initially_published_at] :as card-or-dashboard}] (cond-> card-or-dashboard (and (true? enable_embedding) (nil? initially_published_at)) (assoc :initially_published_at :%now))) |
(defsetting show-static-embed-terms (deferred-tru "Check if the static embedding licensing should be hidden in the static embedding flow") :type :boolean :default true :export? true :getter (fn [] (if-not (and config/ee-available? (:valid (premium-features/token-status))) (setting/get-value-of-type :boolean :show-static-embed-terms) false))) | |