Enterprise specific API utility functions | (ns metabase-enterprise.sandbox.api.util (:require [clojure.set :as set] [medley.core :as m] [metabase.api.common :refer [*current-user-id* *is-superuser?*]] [metabase.models.user :as user] [metabase.permissions.models.data-permissions :as data-perms] [metabase.premium-features.core :refer [defenterprise]] [metabase.util.i18n :refer [tru]] [toucan2.core :as t2])) |
Takes all the group-ids a user belongs to and a sandbox, and determines whether the sandbox should be enforced for the user.
This is done by checking whether any other group provides | (defn- enforce-sandbox?
[{:as _sandbox :keys [table_id] {:keys [db_id]} :table} user-group-ids group-id->sandboxes group-id->impersonations]
;; If any *other* non-sandboxed groups the user is in provide unrestricted view-data access to the table, we don't
;; enforce the sandbox.
(let [sandboxed-groups (into #{} (for [[group-id sandboxes] group-id->sandboxes
:when (some #(= (:table_id %) table_id) sandboxes)]
group-id))
impersonated-groups (into #{} (for [[group-id impersonations] group-id->impersonations
:when (some #(= (:db_id %) db_id) impersonations)]
group-id))
groups-to-exclude (set/union sandboxed-groups impersonated-groups)
groups-to-check (set/difference user-group-ids groups-to-exclude)]
(if (seq groups-to-check)
(not (data-perms/groups-have-permission-for-table? groups-to-check
:perms/view-data
:unrestricted
db_id
table_id))
true))) |
Given a user-id, returns the set of sandboxes that should be enforced for the provided user ID. This result is cached for the duration of a request in [[metabase.permissions.models.data-permissions/sandboxes-for-user]]. WARNING: This should NOT be used directly for sandboxing enforcement. Use | (defenterprise enforced-sandboxes-for-user
:feature :sandboxes
[user-id]
(when user-id
(let [user-group-ids (user/group-ids user-id)
sandboxes-with-group-ids (t2/hydrate
(t2/select :model/GroupTableAccessPolicy
{:select [[:pgm.group_id :group_id]
[:s.*]]
:from [[:permissions_group_membership :pgm]]
:left-join [[:sandboxes :s] [:= :s.group_id :pgm.group_id]]
:where [:and
[:= :pgm.user_id user-id]]})
:table)
impersonations-with-group-ids (t2/select :model/ConnectionImpersonation
:group_id [:in user-group-ids])
group-id->impersonations (->> impersonations-with-group-ids
(group-by :group_id))
group-id->sandboxes (->> sandboxes-with-group-ids
(group-by :group_id)
(m/map-vals (fn [sandboxes]
(->> sandboxes
(filter :table_id)
(into #{})))))]
(filter #(enforce-sandbox? % user-group-ids group-id->sandboxes group-id->impersonations)
(reduce set/union #{} (vals group-id->sandboxes)))))) |
Given collection of table-ids, return the sandboxes that should be enforced for the current user on any of the tables. A sandbox is not enforced if the user is in a different permissions group that grants full access to the table. | (defn enforced-sandboxes-for-tables
[table-ids]
(let [enforced-sandboxes-for-user @data-perms/*sandboxes-for-user*]
(filter #((set table-ids) (:table_id %)) enforced-sandboxes-for-user))) |
Returns true if the currently logged in user has any enforced sandboxes for the provided database. Throws an exception if no current user is bound. | (defn sandboxed-user-for-db?
[database-id]
(when-not *is-superuser?*
(if *current-user-id*
(let [sandboxes (t2/hydrate (seq @data-perms/*sandboxes-for-user*) :table)]
(some #(= (get-in % [:table :db_id]) database-id)
sandboxes))
;; If no *current-user-id* is bound we can't check for sandboxes, so we should throw in this case to avoid
;; returning `false` for users who should actually be sandboxes.
(throw (ex-info (str (tru "No current user found"))
{:status-code 403}))))) |
Returns true if the currently logged in user has any enforced sandboxes. Throws an exception if no current user is bound. | (defenterprise sandboxed-user?
:feature :sandboxes
[]
(boolean
(when-not *is-superuser?*
(if *current-user-id*
(seq @data-perms/*sandboxes-for-user*)
;; If no *current-user-id* is bound we can't check for sandboxes, so we should throw in this case to avoid
;; returning `false` for users who should actually be sandboxes.
(throw (ex-info (str (tru "No current user found"))
{:status-code 403})))))) |