airgap token check user creation fix (#66770)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
bryan
2025-12-23 15:12:46 -07:00
committed by GitHub
parent fdabf871f3
commit b149b004b0
7 changed files with 78 additions and 14 deletions

View File

@@ -30,7 +30,7 @@
(concat ["You can use `./bin/mage <task> --help` to get more information? It works with every task."
"Remember: You can check private tasks with `./bin/mage ls`"
"Fun fact: The word 'mage' comes from the Latin 'magus', meaning 'wise'"
"Pro tip: You can setup autocomplete for mage to speed up your workflow with " (c/cyan "./bin/mage alias.")]
(str "Pro tip: You can setup autocomplete for mage to speed up your workflow with " (c/cyan "./bin/mage alias."))]
(zen))))
(defn- print-help []

View File

@@ -133,6 +133,7 @@
from {
opacity: 0;
}
to {
opacity: 1;
}
@@ -142,6 +143,7 @@
from {
opacity: 1;
}
to {
opacity: 0;
}

View File

@@ -21,7 +21,7 @@
[metabase.logger.core :as logger]
[metabase.notification.core :as notification]
[metabase.plugins.core :as plugins]
[metabase.premium-features.core :as premium-features :refer [defenterprise]]
[metabase.premium-features.core :refer [defenterprise]]
[metabase.sample-data.core :as sample-data]
[metabase.server.core :as server]
[metabase.settings.core :as setting]
@@ -182,8 +182,6 @@
(log/info "Setting up prometheus metrics")
(analytics/setup!)
(init-status/set-progress! 0.5)
(premium-features/airgap-check-user-count)
(init-status/set-progress! 0.55)
(task/init-scheduler!)
(analytics/add-listeners-to-scheduler!)
;; run a very quick check to see if we are doing a first time installation

View File

@@ -19,7 +19,8 @@
[metabase.premium-features.token-check
;; TODO: move airgap code to a dedicated namespace?
airgap-check-user-count
assert-valid-airgap-user-count!
assert-airgap-allows-user-creation!
assert-has-feature
assert-has-any-features
ee-feature-error

View File

@@ -169,7 +169,6 @@
(let [{:keys [body status] :as resp} (http-fetch base-url token site-uuid)]
(cond
(http/success? resp) (some-> body json/decode+kw)
;; todo: what happens if there's no response here? probably should or here
(<= 400 status 499) (or (some-> body json/decode+kw)
{:valid false
:status "Unable to validate token"
@@ -191,14 +190,31 @@
(let [max-users (:max-users (decode-airgap-token token))]
(when (pos? max-users) max-users)))))
(defn airgap-check-user-count
"Checks that, when in an airgap context, the allowed user count is acceptable."
(defn- active-user-count []
;; Because we need this count *during* token checks, this uses `t2/table-name` to avoid the `after-select` method
;; on users, which calls an EE method that needs ... a token check :|
(t2/count (t2/table-name :model/User) :is_active true, :type "personal"))
(defn assert-valid-airgap-user-count!
"Asserts that, in an airgap context, the current user count does not exceed the allowed maximum.
Throws if the limit is exceeded. Called when setting the token."
[]
(when-let [max-users (max-users-allowed)]
;; Because we need this count *during* token checks, this uses `t2/table-name` to avoid the `after-select` method
;; on users, which calls an EE method that needs ... a token check :|
(when (> (t2/count (t2/table-name :model/User) :is_active true, :type "personal") max-users)
(throw (Exception. (trs "You have reached the maximum number of users ({0}) for your plan. Please upgrade to add more users." max-users))))))
(let [current-users (active-user-count)]
(when (> current-users max-users)
(throw (Exception.
(trs "You have reached the maximum number of users ({0}) for your plan. Please upgrade to add more users."
max-users)))))))
(defn assert-airgap-allows-user-creation!
"Asserts that creating a new personal user would not exceed the airgap user limit.
Throws if adding another user would overflow the limit. Called in user pre-insert."
[]
(when-let [max-users (max-users-allowed)]
(when (>= (active-user-count) max-users)
(throw (Exception.
(trs "Adding another user would exceed the maximum number of users ({0}) for your plan. Please upgrade to add more users."
max-users))))))
(mu/defn- decode-token* :- TokenStatus
"Decode a token. If you get a positive response about the token, even if it is not valid, return that. Errors will
@@ -422,7 +438,7 @@
(try
(when (seq new-value)
(when (mr/validate [:re AirgapToken] new-value)
(airgap-check-user-count))
(assert-valid-airgap-user-count!))
(when-not (or (mr/validate [:re RemoteCheckedToken] new-value)
(mr/validate [:re AirgapToken] new-value))
(throw (ex-info (tru "Token format is invalid.")

View File

@@ -120,7 +120,8 @@
(validate-user-type! user-type)
(validate-user-locale! locale)
(validate-sso-setup! sso_source)
(premium-features/airgap-check-user-count))
(when (or (nil? user-type) (= user-type :personal))
(premium-features/assert-airgap-allows-user-creation!)))
;;; -------------------------------------------------- Password Management --------------------------------------------------

View File

@@ -255,3 +255,49 @@
(is (not (mr/validate [:re @#'token-check/RemoteCheckedToken] (apply str (repeat 65 "a")))))
(is (not (mr/validate [:re @#'token-check/RemoteCheckedToken] (apply str (repeat 63 "a")))))
(is (not (mr/validate [:re @#'token-check/RemoteCheckedToken] (apply str "mb_dev_" (repeat 53 "a")))))))
(deftest assert-valid-airgap-user-count-test
(testing "no limit set - no error"
(with-redefs [token-check/max-users-allowed (constantly nil)]
(is (nil? (token-check/assert-valid-airgap-user-count!)))))
(testing "under limit - no error"
(with-redefs [token-check/max-users-allowed (constantly 10)
token-check/active-user-count (constantly 5)]
(is (nil? (token-check/assert-valid-airgap-user-count!)))))
(testing "at limit - no error"
(with-redefs [token-check/max-users-allowed (constantly 10)
token-check/active-user-count (constantly 10)]
(is (nil? (token-check/assert-valid-airgap-user-count!)))))
(testing "over limit - throws"
(with-redefs [token-check/max-users-allowed (constantly 10)
token-check/active-user-count (constantly 11)]
(is (thrown-with-msg? Exception
#"You have reached the maximum number of users"
(token-check/assert-valid-airgap-user-count!))))))
(deftest assert-airgap-allows-user-creation-test
(testing "no limit set - no error"
(with-redefs [token-check/max-users-allowed (constantly nil)]
(is (nil? (token-check/assert-airgap-allows-user-creation!)))))
(testing "under limit - no error (room for one more)"
(with-redefs [token-check/max-users-allowed (constantly 10)
token-check/active-user-count (constantly 9)]
(is (nil? (token-check/assert-airgap-allows-user-creation!)))))
(testing "at limit - throws (no room for another)"
(with-redefs [token-check/max-users-allowed (constantly 10)
token-check/active-user-count (constantly 10)]
(is (thrown-with-msg? Exception
#"Adding another user would exceed the maximum"
(token-check/assert-airgap-allows-user-creation!)))))
(testing "over limit - throws"
(with-redefs [token-check/max-users-allowed (constantly 10)
token-check/active-user-count (constantly 11)]
(is (thrown-with-msg? Exception
#"Adding another user would exceed the maximum"
(token-check/assert-airgap-allows-user-creation!))))))