♻️ Rename nitrate config to admin-console (#10929)

* ♻️ Rename nitrate config to admin-console

Rename user-facing configuration from 'nitrate' to 'admin-console':
- Feature flags: :nitrate -> :admin-console, :nitrate-bulk-create-profiles -> :admin-console-bulk-create-profiles
- Config keys: :nitrate-shared-key -> :admin-console-shared-key, :nitrate-backend-uri -> :admin-console-uri
- Shared-keys map entry: :nitrate -> :admin-console (setup.clj + main.clj)
- Env vars: PENPOT_NITRATE_SHARED_KEY -> PENPOT_ADMIN_CONSOLE_SHARED_KEY, PENPOT_NITRATE_BACKEND_URI removed (consolidated into PENPOT_ADMIN_CONSOLE_URI)
- Docker/nginx: PENPOT_NITRATE_URI -> PENPOT_ADMIN_CONSOLE_URI

Code namespaces, file paths, CSS classes, and i18n keys stay as-is.

AI-assisted-by: mimo-v2.5-pro

* ♻️ Rename initialize-user-in-nitrate-organization to initialize-user-in-organization

Part of the nitrate -> admin-console rename series. The function and all 9 references across 6 files have been renamed.

* ♻️ Rename :nitrate-bulk-create-profiles-not-allowed to :bulk-create-profiles-not-allowed

* ♻️ Inline nitrate-permissions into app.common.types.organization

- Delete app.common.types.nitrate-permissions and its test
- Move permission rules (allowed?, can-send-invitations?, etc.) into organization.cljc
- Harmonize all consumers to use alias cto for app.common.types.organization
- Update test runner and create organization_test.cljc
This commit is contained in:
Andrey Antukh 2026-08-03 17:23:55 +02:00 committed by GitHub
parent 79da4d274d
commit 49119e0339
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 445 additions and 448 deletions

View File

@ -5,7 +5,7 @@
## Stable namespace map ## Stable namespace map
- `app.common.data` and `app.common.data.macros`: generic data helpers and performance macros that do not depend on Penpot domain entities. - `app.common.data` and `app.common.data.macros`: generic data helpers and performance macros that do not depend on Penpot domain entities.
- `app.common.types.*`: shared shape/file/page/component/token data types, schemas, predicates, and entity-local operations. `app.common.types.nitrate-permissions` contains shared fail-closed Nitrate organization/team permission rules. - `app.common.types.*`: shared shape/file/page/component/token data types, schemas, predicates, and entity-local operations. `app.common.types.organization` contains organization schemas, `apply-organization`, and fail-closed organization/team permission rules (`allowed?`, `can-send-invitations?`).
- `app.common.files.*`: file-level operations, shape tree helpers, change application, migrations, validation, and undo/redo-related logic. - `app.common.files.*`: file-level operations, shape tree helpers, change application, migrations, validation, and undo/redo-related logic.
- `app.common.logic.*`: higher-level workflows/algorithms over files, shapes, components, variants, libraries, tokens, etc. - `app.common.logic.*`: higher-level workflows/algorithms over files, shapes, components, variants, libraries, tokens, etc.
- `app.common.geom.*`: geometry helpers and transformations. - `app.common.geom.*`: geometry helpers and transformations.

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export PENPOT_NITRATE_SHARED_KEY=super-secret-nitrate-api-key export PENPOT_ADMIN_CONSOLE_SHARED_KEY=super-secret-nitrate-api-key
export PENPOT_EXPORTER_SHARED_KEY=super-secret-exporter-api-key export PENPOT_EXPORTER_SHARED_KEY=super-secret-exporter-api-key
export PENPOT_NEXUS_SHARED_KEY=super-secret-nexus-api-key export PENPOT_NEXUS_SHARED_KEY=super-secret-nexus-api-key
export PENPOT_SECRET_KEY=super-secret-devenv-key export PENPOT_SECRET_KEY=super-secret-devenv-key
@ -71,7 +71,7 @@ export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
export PENPOT_USER_FEEDBACK_DESTINATION="support@example.com" export PENPOT_USER_FEEDBACK_DESTINATION="support@example.com"
export PENPOT_NITRATE_BACKEND_URI=http://localhost:3000/admin-console export PENPOT_ADMIN_CONSOLE_URI=http://localhost:3000/admin-console
export JAVA_OPTS="\ export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \ -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \

View File

@ -119,7 +119,7 @@
[:allowed-origins {:optional true} [::sm/set :string]] [:allowed-origins {:optional true} [::sm/set :string]]
[:exporter-shared-key {:optional true} :string] [:exporter-shared-key {:optional true} :string]
[:nitrate-shared-key {:optional true} :string] [:admin-console-shared-key {:optional true} :string]
[:nexus-shared-key {:optional true} :string] [:nexus-shared-key {:optional true} :string]
[:management-api-key {:optional true} :string] [:management-api-key {:optional true} :string]
@ -264,7 +264,7 @@
[:netty-io-threads {:optional true} ::sm/int] [:netty-io-threads {:optional true} ::sm/int]
[:nitrate-backend-uri {:optional true} ::sm/uri] [:admin-console-uri {:optional true} ::sm/uri]
;; DEPRECATED ;; DEPRECATED
[:assets-storage-backend {:optional true} :keyword] [:assets-storage-backend {:optional true} :keyword]

View File

@ -469,7 +469,7 @@
::setup/shared-keys ::setup/shared-keys
{::setup/props (ig/ref ::setup/props) {::setup/props (ig/ref ::setup/props)
:nexus (cf/get :nexus-shared-key) :nexus (cf/get :nexus-shared-key)
:nitrate (cf/get :nitrate-shared-key) :admin-console (cf/get :admin-console-shared-key)
:exporter (cf/get :exporter-shared-key)} :exporter (cf/get :exporter-shared-key)}
::setup/clock ::setup/clock

View File

@ -49,7 +49,7 @@
"Joins relative path segments to the Nitrate backend URI. "Joins relative path segments to the Nitrate backend URI.
Segments must not start with `/`" Segments must not start with `/`"
[& segments] [& segments]
(apply join-base-uri (cf/get :nitrate-backend-uri) segments)) (apply join-base-uri (cf/get :admin-console-uri) segments))
(defn- generate-public-uri (defn- generate-public-uri
"Joins relative path segments to the public backend URI. "Joins relative path segments to the public backend URI.
@ -143,7 +143,7 @@
(defn- request-to-nitrate (defn- request-to-nitrate
[cfg method uri schema {:keys [::rpc/profile-id request-params throw-on-error?] :as params}] [cfg method uri schema {:keys [::rpc/profile-id request-params throw-on-error?] :as params}]
(let [shared-key (-> cfg ::setup/shared-keys :nitrate) (let [shared-key (-> cfg ::setup/shared-keys :admin-console)
full-http-call (-> (request-builder cfg method uri shared-key profile-id request-params) full-http-call (-> (request-builder cfg method uri shared-key profile-id request-params)
(with-retries 3) (with-retries 3)
(with-validate uri schema :throw-on-error? throw-on-error?))] (with-validate uri schema :throw-on-error? throw-on-error?))]
@ -155,7 +155,7 @@
(defn call (defn call
[cfg method params] [cfg method params]
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(let [client (get cfg ::client) (let [client (get cfg ::client)
method (get client method)] method (get client method)]
(method params)))) (method params))))
@ -487,7 +487,7 @@
(defmethod ig/init-key ::client (defmethod ig/init-key ::client
[_ cfg] [_ cfg]
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
{:get-team-organization (partial get-team-organization-api cfg) {:get-team-organization (partial get-team-organization-api cfg)
:set-team-organization (partial set-team-organization-api cfg) :set-team-organization (partial set-team-organization-api cfg)
:get-organization-membership (partial get-organization-membership-api cfg) :get-organization-membership (partial get-organization-membership-api cfg)
@ -549,7 +549,7 @@
callers are unaffected. Returns false when the :nitrate flag is off." callers are unaffected. Returns false when the :nitrate flag is off."
[cfg profile-id team-id] [cfg profile-id team-id]
(boolean (boolean
(when (and (contains? cf/flags :nitrate) (when (and (contains? cf/flags :admin-console)
(nitrate-client? cfg) (nitrate-client? cfg)
(some? team-id) (some? team-id)
(some? profile-id)) (some? profile-id))

View File

@ -279,7 +279,7 @@
Raises :nitrate-sso-required error if user is not authorized in the organization." Raises :nitrate-sso-required error if user is not authorized in the organization."
[_ f mdata] [_ f mdata]
(if (and (contains? cf/flags :nitrate) (if (and (contains? cf/flags :admin-console)
(::auth mdata true) ;; only for endpoints that needs auth (::auth mdata true) ;; only for endpoints that needs auth
(::nitrate/sso mdata true)) (::nitrate/sso mdata true))
(fn [cfg params] (fn [cfg params]
@ -430,7 +430,7 @@
[cfg] [cfg]
(let [cfg (assoc cfg ::module "management" ::type "command" ::metrics-id :rpc-management-timing) (let [cfg (assoc cfg ::module "management" ::type "command" ::metrics-id :rpc-management-timing)
mods (cond->> (list 'app.rpc.management.exporter) mods (cond->> (list 'app.rpc.management.exporter)
(contains? cf/flags :nitrate) (contains? cf/flags :admin-console)
(cons 'app.rpc.management.nitrate))] (cons 'app.rpc.management.nitrate))]
(->> (apply sv/scan-ns mods) (->> (apply sv/scan-ns mods)

View File

@ -14,7 +14,7 @@
[app.common.json :as json] [app.common.json :as json]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct] [app.common.time :as ct]
[app.common.types.nitrate-permissions :as nitrate-perms] [app.common.types.organization :as cto]
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.nitrate :as nitrate] [app.nitrate :as nitrate]
@ -423,16 +423,16 @@
(assert-not-default-team cfg team-id) (assert-not-default-team cfg team-id)
(assert-membership cfg profile-id organization-id) (assert-membership cfg profile-id organization-id)
;; Check moveTeams permission on the source organization ;; Check moveTeams permission on the source organization
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(let [organization-perms (nitrate/call cfg :get-organization-permissions (let [organization-perms (nitrate/call cfg :get-organization-permissions
{:organization-id organization-id})] {:organization-id organization-id})]
(if (nil? organization-perms) (if (nil? organization-perms)
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "Unable to verify organization permissions") :hint "Unable to verify organization permissions")
(when-not (nitrate-perms/allowed? :move-team (when-not (cto/allowed? :move-team
{:organization-perms organization-perms {:organization-perms organization-perms
:profile-id profile-id}) :profile-id profile-id})
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "You are not allowed to move teams that are part of this organization. If you need more information, contact the owner."))))) :hint "You are not allowed to move teams that are part of this organization. If you need more information, contact the owner.")))))
@ -462,7 +462,7 @@
Returns {:allows-anybody bool :external-emails [...]}" Returns {:allows-anybody bool :external-emails [...]}"
[{:keys [::db/conn] :as cfg} team-id organization-id] [{:keys [::db/conn] :as cfg} team-id organization-id]
(let [organization-perms (nitrate/call cfg :get-organization-permissions {:organization-id organization-id}) (let [organization-perms (nitrate/call cfg :get-organization-permissions {:organization-id organization-id})
allows-anybody (nitrate-perms/allowed? :add-anybody-to-team {:organization-perms organization-perms})] allows-anybody (cto/allowed? :add-anybody-to-team {:organization-perms organization-perms})]
(if allows-anybody (if allows-anybody
{:allows-anybody true :external-emails []} {:allows-anybody true :external-emails []}
(let [emails (map :email (noh/get-team-invitation-emails conn team-id))] (let [emails (map :email (noh/get-team-invitation-emails conn team-id))]
@ -493,7 +493,7 @@
(assert-not-default-team cfg team-id) (assert-not-default-team cfg team-id)
(assert-membership cfg profile-id organization-id) (assert-membership cfg profile-id organization-id)
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(let [organization-member-ids-before (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id})) (let [organization-member-ids-before (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id}))
team-with-organization (nitrate/call cfg :get-team-organization {:team-id team-id}) team-with-organization (nitrate/call cfg :get-team-organization {:team-id team-id})
source-organization-id (get-in team-with-organization [:organization :id]) source-organization-id (get-in team-with-organization [:organization :id])
@ -517,18 +517,18 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "Unable to verify organization permissions")) :hint "Unable to verify organization permissions"))
(when-not (nitrate-perms/allowed? :move-team (when-not (cto/allowed? :move-team
{:organization-perms source-organization-perms {:organization-perms source-organization-perms
:profile-id profile-id :profile-id profile-id
:target-organization-same-owner? target-organization-same-owner?}) :target-organization-same-owner? target-organization-same-owner?})
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "You are not allowed to move teams that are part of this organization. If you need more information, contact the owner."))) :hint "You are not allowed to move teams that are part of this organization. If you need more information, contact the owner.")))
;; Always check target create-teams permission (new/add and move flows). ;; Always check target create-teams permission (new/add and move flows).
(when-not (nitrate-perms/allowed? :create-team (when-not (cto/allowed? :create-team
{:organization-perms target-organization-perms {:organization-perms target-organization-perms
:profile-id profile-id}) :profile-id profile-id})
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "You are not allowed to add teams in this organization")) :hint "You are not allowed to add teams in this organization"))
@ -540,7 +540,7 @@
(remove #{profile-id}) (remove #{profile-id})
(remove organization-member-ids-before))] (remove organization-member-ids-before))]
(doseq [member-id new-member-ids] (doseq [member-id new-member-ids]
(teams/initialize-user-in-nitrate-organization cfg member-id organization-id))) (teams/initialize-user-in-organization cfg member-id organization-id)))
;; Api call to nitrate ;; Api call to nitrate
(let [team (nitrate/call cfg :set-team-organization {:team-id team-id (let [team (nitrate/call cfg :set-team-organization {:team-id team-id
@ -574,7 +574,7 @@
::sm/result [:map-of :string :boolean] ::sm/result [:map-of :string :boolean]
::db/transaction true} ::db/transaction true}
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id organization-id emails]}] [{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id organization-id emails]}]
(or (when (contains? cf/flags :nitrate) (or (when (contains? cf/flags :admin-console)
(assert-membership cfg profile-id organization-id) (assert-membership cfg profile-id organization-id)
(let [emails-array (db/create-array conn "text" emails) (let [emails-array (db/create-array conn "text" emails)
profiles (db/exec! conn [sql:get-profiles-by-emails emails-array]) profiles (db/exec! conn [sql:get-profiles-by-emails emails-array])
@ -598,7 +598,7 @@
::sm/params schema:all-organization-members-in-team-params ::sm/params schema:all-organization-members-in-team-params
::sm/result ::sm/boolean} ::sm/result ::sm/boolean}
[cfg {:keys [::rpc/profile-id team-id organization-id]}] [cfg {:keys [::rpc/profile-id team-id organization-id]}]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(let [perms (teams/get-permissions cfg profile-id team-id)] (let [perms (teams/get-permissions cfg profile-id team-id)]
(when-not (or (:is-admin perms) (:is-owner perms)) (when-not (or (:is-admin perms) (:is-owner perms))
(ex/raise :type :validation (ex/raise :type :validation
@ -622,7 +622,7 @@
::sm/params schema:all-team-members-in-organizations-params ::sm/params schema:all-team-members-in-organizations-params
::sm/result [:map-of ::sm/uuid ::sm/boolean]} ::sm/result [:map-of ::sm/uuid ::sm/boolean]}
[cfg {:keys [::rpc/profile-id team-id organization-ids]}] [cfg {:keys [::rpc/profile-id team-id organization-ids]}]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(let [perms (teams/get-permissions cfg profile-id team-id)] (let [perms (teams/get-permissions cfg profile-id team-id)]
(when-not (or (:is-admin perms) (:is-owner perms)) (when-not (or (:is-admin perms) (:is-owner perms))
(ex/raise :type :validation (ex/raise :type :validation
@ -659,7 +659,7 @@
::sm/result schema:check-team-external-invitations-result ::sm/result schema:check-team-external-invitations-result
::db/transaction true} ::db/transaction true}
[cfg {:keys [::rpc/profile-id team-id organization-id]}] [cfg {:keys [::rpc/profile-id team-id organization-id]}]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(let [perms (teams/get-permissions cfg profile-id team-id)] (let [perms (teams/get-permissions cfg profile-id team-id)]
(when-not (or (:is-admin perms) (:is-owner perms)) (when-not (or (:is-admin perms) (:is-owner perms))
(ex/raise :type :validation (ex/raise :type :validation
@ -692,7 +692,7 @@
::sm/params schema:check-nitrate-sso ::sm/params schema:check-nitrate-sso
::nitrate/sso false} ::nitrate/sso false}
[cfg {:keys [::rpc/profile-id team-id organization-id url] :as params}] [cfg {:keys [::rpc/profile-id team-id organization-id url] :as params}]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(if (and team-id (if (and team-id
(not (teams/has-read-permissions? cfg profile-id team-id))) (not (teams/has-read-permissions? cfg profile-id team-id)))
;; Let the destination RPC enforce its own permissions. Starting SSO before ;; Let the destination RPC enforce its own permissions. Starting SSO before

View File

@ -96,7 +96,7 @@
(defn- with-nitrate-licence (defn- with-nitrate-licence
[profile cfg] [profile cfg]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(nitrate/add-nitrate-licence-to-profile cfg profile) (nitrate/add-nitrate-licence-to-profile cfg profile)
profile)) profile))
@ -503,7 +503,7 @@
;; imported "Your Penpot" teams according to whether they still have files. ;; imported "Your Penpot" teams according to whether they still have files.
;; Let Nitrate clean up the data associated with the deleted Penpot user: ;; Let Nitrate clean up the data associated with the deleted Penpot user:
;; owned organizations, remaining memberships, and subscription cancellation. ;; owned organizations, remaining memberships, and subscription cancellation.
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(nitrate/call cfg :cleanup-deleted-penpot-user (nitrate/call cfg :cleanup-deleted-penpot-user
{:profile-id profile-id})) {:profile-id profile-id}))
@ -562,7 +562,7 @@
{::doc/added "2.18" {::doc/added "2.18"
::sm/result schema:get-owned-organizations-summary-result} ::sm/result schema:get-owned-organizations-summary-result}
[cfg {:keys [::rpc/profile-id]}] [cfg {:keys [::rpc/profile-id]}]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(or (nitrate/call cfg :get-owned-organizations-summary {:profile-id profile-id}) []) (or (nitrate/call cfg :get-owned-organizations-summary {:profile-id profile-id}) [])
[])) []))

View File

@ -12,7 +12,7 @@
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct] [app.common.time :as ct]
[app.common.types.nitrate-permissions :as nitrate-perms] [app.common.types.organization :as cto]
[app.common.types.team :as types.team] [app.common.types.team :as types.team]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@ -197,9 +197,9 @@
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
(dm/with-open [conn (db/open pool)] (dm/with-open [conn (db/open pool)]
(cond->> (get-teams conn profile-id) (cond->> (get-teams conn profile-id)
(contains? cf/flags :nitrate) (contains? cf/flags :admin-console)
(map #(nitrate/add-organization-info-to-team cfg % params)) (map #(nitrate/add-organization-info-to-team cfg % params))
(contains? cf/flags :nitrate) (contains? cf/flags :admin-console)
(remove #(get-in % [:organization :expired-license]))))) (remove #(get-in % [:organization :expired-license])))))
(def ^:private sql:get-owned-teams (def ^:private sql:get-owned-teams
@ -244,7 +244,7 @@
::sm/params schema:get-team} ::sm/params schema:get-team}
[cfg {:keys [::rpc/profile-id id file-id] :as params}] [cfg {:keys [::rpc/profile-id id file-id] :as params}]
(let [team (get-team cfg :profile-id profile-id :team-id id :file-id file-id)] (let [team (get-team cfg :profile-id profile-id :team-id id :file-id file-id)]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(nitrate/add-organization-info-to-team cfg team params) (nitrate/add-organization-info-to-team cfg team params)
team))) team)))
@ -537,16 +537,16 @@
;; When creating inside an organization, verify the user has permission to do so. ;; When creating inside an organization, verify the user has permission to do so.
;; Fail closed: if organization permissions cannot be fetched, deny the operation. ;; Fail closed: if organization permissions cannot be fetched, deny the operation.
(when (and organization-id (contains? cf/flags :nitrate)) (when (and organization-id (contains? cf/flags :admin-console))
(let [organization-perms (nitrate/call cfg :get-organization-permissions (let [organization-perms (nitrate/call cfg :get-organization-permissions
{:organization-id organization-id})] {:organization-id organization-id})]
(if (nil? organization-perms) (if (nil? organization-perms)
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "Unable to verify organization permissions") :hint "Unable to verify organization permissions")
(when-not (nitrate-perms/allowed? :create-team (when-not (cto/allowed? :create-team
{:organization-perms organization-perms {:organization-perms organization-perms
:profile-id profile-id}) :profile-id profile-id})
(ex/raise :type :validation (ex/raise :type :validation
:code :not-allowed :code :not-allowed
:hint "You are not allowed to create teams in this organization"))))) :hint "You are not allowed to create teams in this organization")))))
@ -579,15 +579,15 @@
team (create-team cfg params)] team (create-team cfg params)]
(select-keys team [:id]))) (select-keys team [:id])))
(defn initialize-user-in-nitrate-organization (defn initialize-user-in-organization
"If needed, create a default team for the user on the organization, "If needed, create a default team for the user on the organization,
and notify Nitrate that a user has been added to an organization." and initialize the user in the organization."
([cfg profile-id organization-id] ([cfg profile-id organization-id]
(initialize-user-in-nitrate-organization cfg profile-id organization-id nil)) (initialize-user-in-organization cfg profile-id organization-id nil))
([cfg profile-id organization-id email] ([cfg profile-id organization-id email]
(assert (db/connection-map? cfg) (assert (db/connection-map? cfg)
"expected cfg with valid connection") "expected cfg with valid connection")
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(db/tx-run! (db/tx-run!
cfg cfg
(fn [{:keys [::db/conn] :as tx-cfg}] (fn [{:keys [::db/conn] :as tx-cfg}]
@ -621,13 +621,13 @@
([{:keys [::db/conn] :as cfg} {:keys [:profile-id :team-id] :as params} options] ([{:keys [::db/conn] :as cfg} {:keys [:profile-id :team-id] :as params} options]
(assert (db/connection-map? cfg) (assert (db/connection-map? cfg)
"expected cfg with valid connection") "expected cfg with valid connection")
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(let [membership (nitrate/call cfg :get-organization-membership-by-team {:profile-id profile-id :team-id team-id})] (let [membership (nitrate/call cfg :get-organization-membership-by-team {:profile-id profile-id :team-id team-id})]
;; Only when the team belong to an organization and the user is not a member ;; Only when the team belong to an organization and the user is not a member
(when (and (when (and
(some? (:organization-id membership)) ;; the team do belong to an organization (some? (:organization-id membership)) ;; the team do belong to an organization
(not (:is-member membership))) ;; the user is not a member of the organization yet (not (:is-member membership))) ;; the user is not a member of the organization yet
(initialize-user-in-nitrate-organization cfg profile-id (:organization-id membership))))) (initialize-user-in-organization cfg profile-id (:organization-id membership)))))
(db/insert! conn :team-profile-rel (assoc params :id (uuid/next)) options))) (db/insert! conn :team-profile-rel (assoc params :id (uuid/next)) options)))
(defn create-team (defn create-team
@ -643,7 +643,7 @@
project (create-team-default-project conn params)] project (create-team-default-project conn params)]
(create-team-role cfg params) (create-team-role cfg params)
;; Set team organization in Nitrate if organization-id is provided ;; Set team organization in Nitrate if organization-id is provided
(when (and (contains? cf/flags :nitrate) (:organization-id params)) (when (and (contains? cf/flags :admin-console) (:organization-id params))
(nitrate/set-team-organization cfg team params)) (nitrate/set-team-organization cfg team params))
(assoc team :default-project-id (:id project)))) (assoc team :default-project-id (:id project))))
@ -803,19 +803,19 @@
[{:keys [::db/conn] :as cfg} {:keys [profile-id team-id] :as params}] [{:keys [::db/conn] :as cfg} {:keys [profile-id team-id] :as params}]
(let [team (get-team conn :profile-id profile-id :team-id team-id) (let [team (get-team conn :profile-id profile-id :team-id team-id)
team (if (contains? cf/flags :nitrate) team (if (contains? cf/flags :admin-console)
(nitrate/add-organization-info-to-team cfg team params) (nitrate/add-organization-info-to-team cfg team params)
team) team)
perms (get team :permissions) perms (get team :permissions)
organization (:organization team) organization (:organization team)
in-organization? (and (contains? cf/flags :nitrate) organization) in-organization? (and (contains? cf/flags :admin-console) organization)
can-delete? can-delete?
(if in-organization? (if in-organization?
(nitrate-perms/allowed? :delete-team (cto/allowed? :delete-team
{:organization-perms {:owner-id (dm/get-in team [:organization :owner-id]) {:organization-perms {:owner-id (dm/get-in team [:organization :owner-id])
:permissions (dm/get-in team [:organization :permissions])} :permissions (dm/get-in team [:organization :permissions])}
:profile-id profile-id :profile-id profile-id
:team-perms perms}) :team-perms perms})
(boolean (:is-owner perms)))] (boolean (:is-owner perms)))]
(when-not can-delete? (when-not can-delete?
@ -836,7 +836,7 @@
{::db/return-keys true})] {::db/return-keys true})]
;; Api call to nitrate ;; Api call to nitrate
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(nitrate/call cfg :delete-team {:profile-id profile-id :team-id team-id})) (nitrate/call cfg :delete-team {:profile-id profile-id :team-id team-id}))
(wrk/submit! {::db/conn conn (wrk/submit! {::db/conn conn

View File

@ -14,7 +14,7 @@
[app.common.logging :as l] [app.common.logging :as l]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct] [app.common.time :as ct]
[app.common.types.nitrate-permissions :as nitrate-perms] [app.common.types.organization :as cto]
[app.common.types.team :as types.team] [app.common.types.team :as types.team]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@ -147,7 +147,7 @@
;; When nitrate is active and the team belongs to an organization, check that ;; When nitrate is active and the team belongs to an organization, check that
;; the email is already an organization member unless the organization explicitly allows adding anybody. ;; the email is already an organization member unless the organization explicitly allows adding anybody.
(when (and (contains? cf/flags :nitrate) (when (and (contains? cf/flags :admin-console)
(:organization team)) (:organization team))
(assert-email-can-be-invited member organization-member-ids)) (assert-email-can-be-invited member organization-member-ids))
@ -166,8 +166,8 @@
(if organization (if organization
;; Insert the invited member to the organization ;; Insert the invited member to the organization
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(teams/initialize-user-in-nitrate-organization cfg (:id member) (:id organization) email)) (teams/initialize-user-in-organization cfg (:id member) (:id organization) email))
;; Insert the invited member to the team ;; Insert the invited member to the team
(teams/add-profile-to-team! cfg params {::db/on-conflict-do-nothing? true})) (teams/add-profile-to-team! cfg params {::db/on-conflict-do-nothing? true}))
@ -253,7 +253,7 @@
(when (allow-invitation-emails? member) (when (allow-invitation-emails? member)
(if organization (if organization
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(eml/send! {::eml/conn conn (eml/send! {::eml/conn conn
::eml/factory eml/invite-to-organization ::eml/factory eml/invite-to-organization
:public-uri (cf/get :public-uri) :public-uri (cf/get :public-uri)
@ -346,12 +346,12 @@
- invitations (vector of {:email :role} maps)" - invitations (vector of {:email :role} maps)"
[{:keys [::db/conn] :as cfg} {:keys [profile team role emails invitations] :as params}] [{:keys [::db/conn] :as cfg} {:keys [profile team role emails invitations] :as params}]
(let [;; Enrich team with organization info once for all invitations when nitrate is active (let [;; Enrich team with organization info once for all invitations when nitrate is active
team (if (contains? cf/flags :nitrate) team (if (contains? cf/flags :admin-console)
(nitrate/add-organization-info-to-team cfg team {}) (nitrate/add-organization-info-to-team cfg team {})
team) team)
organization (:organization team) organization (:organization team)
organization-id (:id organization) organization-id (:id organization)
restricted? (and organization-id (not (nitrate-perms/allowed? :add-anybody-to-team {:organization-perms organization}))) restricted? (and organization-id (not (cto/allowed? :add-anybody-to-team {:organization-perms organization})))
all-organization-member-ids all-organization-member-ids
(when organization-id (when organization-id
(into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id}))) (into #{} (nitrate/call cfg :get-organization-members {:organization-id organization-id})))

View File

@ -87,7 +87,7 @@
(defn- with-nitrate-licence (defn- with-nitrate-licence
[profile cfg] [profile cfg]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(nitrate/add-nitrate-licence-to-profile cfg profile) (nitrate/add-nitrate-licence-to-profile cfg profile)
profile)) profile))
@ -136,8 +136,8 @@
accepted-team-id (if organization-id accepted-team-id (if organization-id
;; Insert the invited member to the organization ;; Insert the invited member to the organization
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(teams/initialize-user-in-nitrate-organization cfg id-member organization-id member-email)) (teams/initialize-user-in-organization cfg id-member organization-id member-email))
;; Insert the invited member to the team ;; Insert the invited member to the team
(do (teams/add-profile-to-team! cfg params {::db/on-conflict-do-nothing? true}) (do (teams/add-profile-to-team! cfg params {::db/on-conflict-do-nothing? true})
team-id))] team-id))]
@ -206,7 +206,7 @@
{:columns [:id :email :default-team-id]}) {:columns [:id :email :default-team-id]})
registration-disabled? (not (contains? cf/flags :registration)) registration-disabled? (not (contains? cf/flags :registration))
organization-invitation? (and (contains? cf/flags :nitrate) organization-id)] organization-invitation? (and (contains? cf/flags :admin-console) organization-id)]
(if profile (if profile
(do (do
@ -229,7 +229,7 @@
;; would call nitrate needlessly and could mask the clean ;; would call nitrate needlessly and could mask the clean
;; :canceled-invitation/:invalid-token response with a generic error. ;; :canceled-invitation/:invalid-token response with a generic error.
(let [membership (let [membership
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(cond (cond
organization-id organization-id
(nitrate/call cfg :get-organization-membership {:profile-id profile-id (nitrate/call cfg :get-organization-membership {:profile-id profile-id

View File

@ -14,7 +14,7 @@
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct] [app.common.time :as ct]
[app.common.types.organization :refer [schema:team-with-organization schema:organization-with-avatar schema:nitrate-sso]] [app.common.types.organization :as cto]
[app.common.types.profile :refer [schema:profile, schema:basic-profile]] [app.common.types.profile :refer [schema:profile, schema:basic-profile]]
[app.common.types.team :refer [schema:team]] [app.common.types.team :refer [schema:team]]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
@ -152,7 +152,7 @@
(sv/defmethod ::notify-team-change (sv/defmethod ::notify-team-change
"Notify to Penpot a team change from nitrate" "Notify to Penpot a team change from nitrate"
{::doc/added "2.14" {::doc/added "2.14"
::sm/params schema:team-with-organization ::sm/params cto/schema:team-with-organization
::rpc/auth false} ::rpc/auth false}
[cfg team] [cfg team]
(notifications/notify-team-change cfg (select-keys team [:id :is-your-penpot :organization]) nil) (notifications/notify-team-change cfg (select-keys team [:id :is-your-penpot :organization]) nil)
@ -493,7 +493,7 @@ RETURNING id, deleted_at;")
{::doc/added "2.15" {::doc/added "2.15"
::sm/params [:map ::sm/params [:map
[:email ::sm/email] [:email ::sm/email]
[:organization schema:organization-with-avatar]] [:organization cto/schema:organization-with-avatar]]
::nitrate/sso false} ::nitrate/sso false}
[cfg params] [cfg params]
(db/tx-run! cfg ti/create-organization-invitation params) (db/tx-run! cfg ti/create-organization-invitation params)
@ -688,7 +688,7 @@ RETURNING id, deleted_at;")
[:user-name [:maybe ::sm/text]] [:user-name [:maybe ::sm/text]]
[:renewal-date :string] [:renewal-date :string]
[:estimated-amount :double] [:estimated-amount :double]
[:organizations [:vector schema:organization-with-avatar]]]) [:organizations [:vector cto/schema:organization-with-avatar]]])
(sv/defmethod ::send-renewal-email (sv/defmethod ::send-renewal-email
"Send an Enterprise subscription renewal notice email to a user." "Send an Enterprise subscription renewal notice email to a user."
@ -941,7 +941,7 @@ RETURNING id, deleted_at;")
Nitrate calls this while configuring SSO to verify client credentials and OIDC Nitrate calls this while configuring SSO to verify client credentials and OIDC
discovery before saving the settings." discovery before saving the settings."
{::doc/added "2.20" {::doc/added "2.20"
::sm/params schema:nitrate-sso ::sm/params cto/schema:nitrate-sso
::sm/result schema:check-organization-sso-result ::sm/result schema:check-organization-sso-result
::rpc/auth false} ::rpc/auth false}
[cfg params] [cfg params]
@ -997,7 +997,7 @@ RETURNING id, deleted_at;")
created users skip email verification and onboarding. Emails that already created users skip email verification and onboarding. Emails that already
belong to an existing profile are skipped. Intended for the Nitrate admin belong to an existing profile are skipped. Intended for the Nitrate admin
bulk-creation screen; access is gated by the shared key and, in Nitrate, an bulk-creation screen; access is gated by the shared key and, in Nitrate, an
email allow-list. Requires the `nitrate-bulk-create-profiles` flag, disabled email allow-list. Requires the `admin-console-bulk-create-profiles` flag, disabled
by default so it is only available on test environments." by default so it is only available on test environments."
{::doc/added "2.19" {::doc/added "2.19"
::sm/params schema:bulk-create-profiles-params ::sm/params schema:bulk-create-profiles-params
@ -1005,9 +1005,9 @@ RETURNING id, deleted_at;")
::rpc/auth false} ::rpc/auth false}
[cfg {:keys [password emails]}] [cfg {:keys [password emails]}]
(when-not (contains? cf/flags :nitrate-bulk-create-profiles) (when-not (contains? cf/flags :admin-console-bulk-create-profiles)
(ex/raise :type :restriction (ex/raise :type :restriction
:code :nitrate-bulk-create-profiles-not-allowed :code :bulk-create-profiles-not-allowed
:hint "Bulk profile creation is disabled by config.")) :hint "Bulk profile creation is disabled by config."))
(let [derived (aauth/derive-password password)] (let [derived (aauth/derive-password password)]

View File

@ -31,7 +31,7 @@
(defn- organization-sso-active? (defn- organization-sso-active?
"Return whether SSO is enabled for the organization." "Return whether SSO is enabled for the organization."
[cfg organization-id] [cfg organization-id]
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(true? (:active (nitrate/call cfg :get-organization-sso {:organization-id organization-id}))))) (true? (:active (nitrate/call cfg :get-organization-sso {:organization-id organization-id})))))
(def ^:private xf:map-email (map :email)) (def ^:private xf:map-email (map :email))

View File

@ -115,7 +115,7 @@
(assoc keys id key))))) (assoc keys id key)))))
{} {}
[:exporter [:exporter
:nitrate :admin-console
:nexus]))) :nexus])))
(sm/register! ::props [:map-of :keyword ::sm/any]) (sm/register! ::props [:map-of :keyword ::sm/any])

View File

@ -25,7 +25,7 @@
[mockery.core :refer [with-mocks]])) [mockery.core :refer [with-mocks]]))
(t/use-fixtures :once (t/compose-fixtures (t/use-fixtures :once (t/compose-fixtures
(partial th/init-config [:enable-nitrate]) (partial th/init-config [:enable-admin-console])
th/init-system)) th/init-system))
(t/use-fixtures :each th/database-reset) (t/use-fixtures :each th/database-reset)

View File

@ -101,7 +101,7 @@
:team-id (:id team) :team-id (:id team)
:url "https://penpot.example.com/#/workspace"} :url "https://penpot.example.com/#/workspace"}
{::http/request {}})] {::http/request {}})]
(binding [cf/flags (conj cf/flags :nitrate)] (binding [cf/flags (conj cf/flags :admin-console)]
(with-redefs [nitrate/call (with-redefs [nitrate/call
(active-sso-call-mock (active-sso-call-mock
(:id team) (:id team)
@ -124,7 +124,7 @@
:team-id (:id team) :team-id (:id team)
:url "https://penpot.example.com/#/workspace"} :url "https://penpot.example.com/#/workspace"}
{::http/request {}})] {::http/request {}})]
(binding [cf/flags (conj cf/flags :nitrate)] (binding [cf/flags (conj cf/flags :admin-console)]
(with-redefs [nitrate/call (with-redefs [nitrate/call
(active-sso-call-mock (active-sso-call-mock
(:id team) (:id team)
@ -150,7 +150,7 @@
:team-id (:id team) :team-id (:id team)
:url "https://penpot.example.com/#/workspace"} :url "https://penpot.example.com/#/workspace"}
{::http/request {}})] {::http/request {}})]
(binding [cf/flags (conj cf/flags :nitrate)] (binding [cf/flags (conj cf/flags :admin-console)]
(with-redefs [nitrate/organization-owner-of-team? (with-redefs [nitrate/organization-owner-of-team?
(fn [_cfg profile-id team-id] (fn [_cfg profile-id team-id]
(and (= (:id organization-owner) profile-id) (and (= (:id organization-owner) profile-id)
@ -851,7 +851,7 @@
organization-id-1 (uuid/random) organization-id-1 (uuid/random)
organization-id-2 (uuid/random) organization-id-2 (uuid/random)
calls (atom [])] calls (atom [])]
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call (fn [_cfg method params] nitrate/call (fn [_cfg method params]
(swap! calls conj [method params]) (swap! calls conj [method params])
(case method (case method
@ -882,7 +882,7 @@
organization-id-1 (uuid/random) organization-id-1 (uuid/random)
organization-id-2 (uuid/random) organization-id-2 (uuid/random)
calls (atom [])] calls (atom [])]
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call (fn [_cfg method params] nitrate/call (fn [_cfg method params]
(swap! calls conj [method params]) (swap! calls conj [method params])
(case method (case method
@ -990,7 +990,7 @@
:role "editor" :role "editor"
:valid-until (ct/in-future "48h")}) :valid-until (ct/in-future "48h")})
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call (add-team-to-organization-nitrate-mock nitrate/call (add-team-to-organization-nitrate-mock
{:organization-id organization-id {:organization-id organization-id
:organization-summary organization-summary :organization-summary organization-summary
@ -999,7 +999,7 @@
:team-id (:id team) :team-id (:id team)
:sso-active? true :sso-active? true
:set-team-params set-team-params}) :set-team-params set-team-params})
teams/initialize-user-in-nitrate-organization (fn [& _] nil) teams/initialize-user-in-organization (fn [& _] nil)
eml/send! (fn [params] (swap! sent conj params))] eml/send! (fn [params] (swap! sent conj params))]
(let [out (th/command! {::th/type :add-team-to-organization (let [out (th/command! {::th/type :add-team-to-organization
::rpc/profile-id (:id owner) ::rpc/profile-id (:id owner)
@ -1066,7 +1066,7 @@
:role "editor" :role "editor"
:valid-until (ct/in-future "48h")}) :valid-until (ct/in-future "48h")})
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call (add-team-to-organization-nitrate-mock nitrate/call (add-team-to-organization-nitrate-mock
{:organization-id organization-id {:organization-id organization-id
:organization-summary organization-summary :organization-summary organization-summary
@ -1074,7 +1074,7 @@
:owner-id (:id owner) :owner-id (:id owner)
:team-id (:id team) :team-id (:id team)
:sso-active? false}) :sso-active? false})
teams/initialize-user-in-nitrate-organization (fn [& _] nil) teams/initialize-user-in-organization (fn [& _] nil)
eml/send! (fn [params] (swap! sent conj params))] eml/send! (fn [params] (swap! sent conj params))]
(let [out (th/command! {::th/type :add-team-to-organization (let [out (th/command! {::th/type :add-team-to-organization
::rpc/profile-id (:id owner) ::rpc/profile-id (:id owner)
@ -1088,7 +1088,7 @@
nitrate-id "nitrate-instance-1" nitrate-id "nitrate-instance-1"
public-key "-----BEGIN PUBLIC KEY-----\nMIIB\n-----END PUBLIC KEY-----" public-key "-----BEGIN PUBLIC KEY-----\nMIIB\n-----END PUBLIC KEY-----"
now (ct/now)] now (ct/now)]
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
ct/now (constantly now) ct/now (constantly now)
nitrate/call (fn [_cfg method _params] nitrate/call (fn [_cfg method _params]
(t/is (= :get-identity method)) (t/is (= :get-identity method))
@ -1117,7 +1117,7 @@
(t/deftest get-nitrate-activation-code-request-identity-unavailable (t/deftest get-nitrate-activation-code-request-identity-unavailable
(let [profile (th/create-profile* 1 {:is-active true})] (let [profile (th/create-profile* 1 {:is-active true})]
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call (fn [_cfg _method _params] nil)] nitrate/call (fn [_cfg _method _params] nil)]
(let [out (th/command! {::th/type :get-nitrate-activation-code-request (let [out (th/command! {::th/type :get-nitrate-activation-code-request
::rpc/profile-id (:id profile)})] ::rpc/profile-id (:id profile)})]

View File

@ -28,7 +28,7 @@
(defn- with-organization-owner-access (defn- with-organization-owner-access
[{:keys [organization-owner-id organization-id team-id]} f] [{:keys [organization-owner-id organization-id team-id]} f]
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/organization-owner-of-team? nitrate/organization-owner-of-team?
(fn [_cfg profile-id candidate-team-id] (fn [_cfg profile-id candidate-team-id]
(and (= organization-owner-id profile-id) (and (= organization-owner-id profile-id)

View File

@ -91,7 +91,7 @@
(t/is (not (contains? result :password)))))) (t/is (not (contains? result :password))))))
(t/testing "update profile" (t/testing "update profile"
(with-redefs [app.config/flags #{:nitrate}] (with-redefs [app.config/flags #{:admin-console}]
(with-redefs [nitrate/add-nitrate-licence-to-profile (with-redefs [nitrate/add-nitrate-licence-to-profile
(fn [_ profile] (fn [_ profile]
(assoc profile :subscription {:plan :pro}))] (assoc profile :subscription {:plan :pro}))]

View File

@ -137,7 +137,7 @@
:team-id (:id team) :team-id (:id team)
:role :editor :role :editor
:emails [email]}))] :emails [email]}))]
(with-redefs [cf/flags (conj cf/flags :nitrate :email-verification) (with-redefs [cf/flags (conj cf/flags :admin-console :email-verification)
nitrate/call nitrate-call] nitrate/call nitrate-call]
(t/is (th/success? (invite! organization-team (:email invitee)))) (t/is (th/success? (invite! organization-team (:email invitee))))
(t/is (th/success? (invite! organization-team (:email invitee)))) (t/is (th/success? (invite! organization-team (:email invitee))))
@ -482,7 +482,7 @@
:role "editor" :role "editor"
:valid-until (ct/in-future "48h")}) :valid-until (ct/in-future "48h")})
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call nitrate/call
(fn [_cfg method _params] (fn [_cfg method _params]
(case method (case method
@ -490,7 +490,7 @@
:is-member false} :is-member false}
:get-organization-members [(:id inviter) (uuid/random) (uuid/random)] :get-organization-members [(:id inviter) (uuid/random) (uuid/random)]
nil)) nil))
teams/initialize-user-in-nitrate-organization teams/initialize-user-in-organization
(fn [& _] default-team-id)] (fn [& _] default-team-id)]
(let [out (verify! direct-token)] (let [out (verify! direct-token)]
(t/is (th/success? out)) (t/is (th/success? out))
@ -527,7 +527,7 @@
:role "editor" :role "editor"
:valid-until (ct/in-future "48h")}) :valid-until (ct/in-future "48h")})
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call nitrate/call
(fn [_cfg method _params] (fn [_cfg method _params]
(case method (case method
@ -570,7 +570,7 @@
:role "editor" :role "editor"
:valid-until (ct/in-future "48h")}) :valid-until (ct/in-future "48h")})
(with-redefs [cf/flags (conj cf/flags :nitrate) (with-redefs [cf/flags (conj cf/flags :admin-console)
nitrate/call nitrate/call
(fn [_cfg method _params] (fn [_cfg method _params]
(case method (case method

View File

@ -164,13 +164,13 @@
;; redis for caching data ;; redis for caching data
:redis-cache :redis-cache
;; Activates the nitrate module ;; Activates the admin-console module
:nitrate :admin-console
;; disabled by default. When enabled, allows the nitrate ;; disabled by default. When enabled, allows the admin-console
;; `bulk-create-profiles` method to create batches of already ;; `bulk-create-profiles` method to create batches of already
;; active profiles. Only intended for test environments. ;; active profiles. Only intended for test environments.
:nitrate-bulk-create-profiles :admin-console-bulk-create-profiles
:mcp :mcp
:background-blur :background-blur

View File

@ -1,103 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.types.nitrate-permissions)
(def ^:private defaults
{:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "always"
:send-invitations "ownersAndAdmins"
:new-team-members "anyone"})
(defn- can-create-team?
[{:keys [is-organization-owner? permission-value]}]
(or is-organization-owner?
(= permission-value "any")))
(defn- can-delete-team?
[{:keys [is-organization-owner? permission-value team-perms]}]
(cond
;; Organization owners can always delete teams inside their organizations.
is-organization-owner?
true
(= permission-value "onlyOwners")
(boolean (:is-owner team-perms))
:else false))
(defn- can-move-team?
[{:keys [permission-value target-organization-same-owner?]}]
(cond
(= permission-value "never")
false
(= permission-value "always")
true
(= permission-value "myOrganizations")
(true? target-organization-same-owner?)
:else false))
(defn- can-invite-to-team?
[{:keys [permission-value team-perms]}]
(cond
(= permission-value "ownersAndAdmins")
(or (boolean (:is-owner team-perms))
(boolean (:is-admin team-perms)))
(= permission-value "owners")
(boolean (:is-owner team-perms))
:else false))
(defn- can-add-anybody-to-team?
[{:keys [permission-value]}]
(= permission-value "anyone"))
(def ^:private action-rules
{:create-team {:permission-key :create-teams
:check-fn can-create-team?}
:delete-team {:permission-key :delete-teams
:check-fn can-delete-team?}
:move-team {:permission-key :move-teams
:check-fn can-move-team?}
:send-invitations {:permission-key :send-invitations
:check-fn can-invite-to-team?}
:add-anybody-to-team {:permission-key :new-team-members
:check-fn can-add-anybody-to-team?}})
(defn- normalize-organization-permissions
[organization-perms]
(merge defaults (or (:permissions organization-perms) {})))
(defn- owner?
[organization-perms profile-id]
(= profile-id (:owner-id organization-perms)))
(defn allowed?
"Returns true only for explicitly allowed actions (fail-closed)."
[action {:keys [organization-perms profile-id team-perms target-organization-same-owner?]}]
(let [{:keys [permission-key check-fn] :as rule}
(get action-rules action)
permissions (normalize-organization-permissions organization-perms)
is-organization-owner? (owner? organization-perms profile-id)
permission-value (get permissions permission-key)]
(cond
(nil? rule) false
:else (boolean (check-fn {:is-organization-owner? is-organization-owner?
:permission-value permission-value
:team-perms team-perms
:target-organization-same-owner? target-organization-same-owner?})))))
(defn can-send-invitations?
[{:keys [nitrate-enabled? organization profile-id team-permissions]}]
(let [in-organization? (and nitrate-enabled? organization)]
(if in-organization?
(allowed? :send-invitations
{:organization-perms {:owner-id (:owner-id organization)
:permissions (:permissions organization)}
:profile-id profile-id
:team-perms team-permissions})
(or (boolean (:is-owner team-permissions))
(boolean (:is-admin team-permissions))))))

View File

@ -6,6 +6,7 @@
(ns app.common.types.organization (ns app.common.types.organization
(:require (:require
[app.common.flags :as flags]
[app.common.schema :as sm])) [app.common.schema :as sm]))
(def schema:organization (def schema:organization
@ -72,3 +73,104 @@
[:client-id {:optional true} [:maybe :string]] [:client-id {:optional true} [:maybe :string]]
[:client-secret {:optional true} [:maybe :string]] [:client-secret {:optional true} [:maybe :string]]
[:issuer {:optional true} [:maybe :string]]]) [:issuer {:optional true} [:maybe :string]]])
;; --- Organization permission rules ---
(def ^:private defaults
{:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "always"
:send-invitations "ownersAndAdmins"
:new-team-members "anyone"})
(defn- can-create-team?
[{:keys [is-organization-owner? permission-value]}]
(or is-organization-owner?
(= permission-value "any")))
(defn- can-delete-team?
[{:keys [is-organization-owner? permission-value team-perms]}]
(cond
;; Organization owners can always delete teams inside their organizations.
is-organization-owner?
true
(= permission-value "onlyOwners")
(boolean (:is-owner team-perms))
:else false))
(defn- can-move-team?
[{:keys [permission-value target-organization-same-owner?]}]
(cond
(= permission-value "never")
false
(= permission-value "always")
true
(= permission-value "myOrganizations")
(true? target-organization-same-owner?)
:else false))
(defn- can-invite-to-team?
[{:keys [permission-value team-perms]}]
(cond
(= permission-value "ownersAndAdmins")
(or (boolean (:is-owner team-perms))
(boolean (:is-admin team-perms)))
(= permission-value "owners")
(boolean (:is-owner team-perms))
:else false))
(defn- can-add-anybody-to-team?
[{:keys [permission-value]}]
(= permission-value "anyone"))
(def ^:private action-rules
{:create-team {:permission-key :create-teams
:check-fn can-create-team?}
:delete-team {:permission-key :delete-teams
:check-fn can-delete-team?}
:move-team {:permission-key :move-teams
:check-fn can-move-team?}
:send-invitations {:permission-key :send-invitations
:check-fn can-invite-to-team?}
:add-anybody-to-team {:permission-key :new-team-members
:check-fn can-add-anybody-to-team?}})
(defn- normalize-organization-permissions
[organization-perms]
(merge defaults (or (:permissions organization-perms) {})))
(defn- owner?
[organization-perms profile-id]
(= profile-id (:owner-id organization-perms)))
(defn allowed?
"Returns true only for explicitly allowed actions (fail-closed)."
[action {:keys [organization-perms profile-id team-perms target-organization-same-owner?]}]
(let [{:keys [permission-key check-fn] :as rule}
(get action-rules action)
permissions (normalize-organization-permissions organization-perms)
is-organization-owner? (owner? organization-perms profile-id)
permission-value (get permissions permission-key)]
(cond
(nil? rule) false
:else (boolean (check-fn {:is-organization-owner? is-organization-owner?
:permission-value permission-value
:team-perms team-perms
:target-organization-same-owner? target-organization-same-owner?})))))
(defn can-send-invitations?
"Returns true when the user can send invitations to a team.
Falls back to team-level permissions (owner/admin) when the
admin-console flag is off or the team has no organization."
[{:keys [organization profile-id team-permissions]}]
(if (and (contains? flags/*current* :admin-console) organization)
(allowed? :send-invitations
{:organization-perms {:owner-id (:owner-id organization)
:permissions (:permissions organization)}
:profile-id profile-id
:team-perms team-permissions})
(or (boolean (:is-owner team-permissions))
(boolean (:is-admin team-permissions)))))

View File

@ -77,8 +77,8 @@
[common-tests.types.container-test] [common-tests.types.container-test]
[common-tests.types.fill-test] [common-tests.types.fill-test]
[common-tests.types.modifiers-test] [common-tests.types.modifiers-test]
[common-tests.types.nitrate-permissions-test]
[common-tests.types.objects-map-test] [common-tests.types.objects-map-test]
[common-tests.types.organization-test]
[common-tests.types.path-data-test] [common-tests.types.path-data-test]
[common-tests.types.shape-decode-encode-test] [common-tests.types.shape-decode-encode-test]
[common-tests.types.shape-interactions-test] [common-tests.types.shape-interactions-test]
@ -154,8 +154,8 @@
'common-tests.types.container-test 'common-tests.types.container-test
'common-tests.types.fill-test 'common-tests.types.fill-test
'common-tests.types.modifiers-test 'common-tests.types.modifiers-test
'common-tests.types.nitrate-permissions-test
'common-tests.types.objects-map-test 'common-tests.types.objects-map-test
'common-tests.types.organization-test
'common-tests.types.path-data-test 'common-tests.types.path-data-test
'common-tests.types.shape-decode-encode-test 'common-tests.types.shape-decode-encode-test
'common-tests.types.shape-interactions-test 'common-tests.types.shape-interactions-test

View File

@ -1,178 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns common-tests.types.nitrate-permissions-test
(:require
[app.common.types.nitrate-permissions :as nitrate-perms]
[clojure.test :as t]))
(def organization-perms
{:owner-id :owner
:permissions {:create-teams "any"
:delete-teams "onlyOwners"
:send-invitations "ownersAndAdmins"}})
(t/deftest unknown-action-is-denied
(t/is (false? (nitrate-perms/allowed? :unknown
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-admin true}}))))
(t/deftest organization-owner-is-allowed-for-create-and-delete
(t/is (true? (nitrate-perms/allowed? :create-team
{:organization-perms organization-perms
:profile-id :owner
:team-perms {:is-admin false}})))
(t/is (true? (nitrate-perms/allowed? :delete-team
{:organization-perms organization-perms
:profile-id :owner
:team-perms {:is-admin false}}))))
(t/deftest create-team-permission-rules
(t/is (true? (nitrate-perms/allowed? :create-team
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-admin false}})))
(t/is (false? (nitrate-perms/allowed? :create-team
{:organization-perms (assoc organization-perms :permissions {:create-teams "none"
:delete-teams "onlyOwners"})
:profile-id :member
:team-perms {:is-admin false}}))))
(t/deftest delete-team-onlyowners-allows-only-team-owners
(t/is (true? (nitrate-perms/allowed? :delete-team
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))
(t/is (false? (nitrate-perms/allowed? :delete-team
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-admin true}})))
(t/is (false? (nitrate-perms/allowed? :delete-team
{:organization-perms (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "invalid-value"})
:profile-id :member
:team-perms {:is-admin true}}))))
(t/deftest delete-team-onlyme-still-allows-organization-owner
(let [only-me-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyMe"})]
(t/is (true? (nitrate-perms/allowed? :delete-team
{:organization-perms only-me-organization
:profile-id :owner
:team-perms {:is-owner false :is-admin false}})))
(t/is (false? (nitrate-perms/allowed? :delete-team
{:organization-perms only-me-organization
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))))
(t/deftest move-team-always-allows-any-organization-owner-or-all-users
(let [always-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "always"})]
;; Organization owner should always be allowed
(t/is (true? (nitrate-perms/allowed? :move-team
{:organization-perms always-organization
:profile-id :owner
:team-perms {}})))
;; Regular member should be allowed when move-teams is "always"
(t/is (true? (nitrate-perms/allowed? :move-team
{:organization-perms always-organization
:profile-id :member
:team-perms {}})))))
(t/deftest move-team-myorganizations-allows-only-within-same-owner
(let [my-organizations (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "myOrganizations"})]
;; Organization owner must also stay within same-owner organizations
(t/is (false? (nitrate-perms/allowed? :move-team
{:organization-perms my-organizations
:profile-id :owner
:team-perms {}
:target-organization-same-owner? false})))
(t/is (true? (nitrate-perms/allowed? :move-team
{:organization-perms my-organizations
:profile-id :owner
:team-perms {}
:target-organization-same-owner? true})))
;; Regular member should be allowed only if target has same owner
(t/is (true? (nitrate-perms/allowed? :move-team
{:organization-perms my-organizations
:profile-id :member
:team-perms {}
:target-organization-same-owner? true})))
(t/is (false? (nitrate-perms/allowed? :move-team
{:organization-perms my-organizations
:profile-id :member
:team-perms {}
:target-organization-same-owner? false})))))
(t/deftest move-team-never-denies-all
(let [never-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "never"})]
;; Even organization owner should be denied
(t/is (false? (nitrate-perms/allowed? :move-team
{:organization-perms never-organization
:profile-id :owner
:team-perms {}})))
;; Regular member should be denied
(t/is (false? (nitrate-perms/allowed? :move-team
{:organization-perms never-organization
:profile-id :member
:team-perms {}})))))
(t/deftest move-team-defaults-to-always
(let [default-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"})]
;; Should default to "always" when not specified
(t/is (true? (nitrate-perms/allowed? :move-team
{:organization-perms default-organization
:profile-id :member
:team-perms {}})))))
(t/deftest send-invitations-defaults-to-owners-and-admins
(let [default-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"})]
(t/is (true? (nitrate-perms/allowed? :send-invitations
{:organization-perms default-organization
:profile-id :owner
:team-perms {:is-owner true :is-admin false}})))
(t/is (true? (nitrate-perms/allowed? :send-invitations
{:organization-perms default-organization
:profile-id :member
:team-perms {:is-owner false :is-admin true}})))
(t/is (false? (nitrate-perms/allowed? :send-invitations
{:organization-perms default-organization
:profile-id :member
:team-perms {:is-owner false :is-admin false}})))))
(t/deftest send-invitations-owners-allows-only-team-owners
(let [only-owners-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:send-invitations "owners"})]
(t/is (true? (nitrate-perms/allowed? :send-invitations
{:organization-perms only-owners-organization
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))
(t/is (false? (nitrate-perms/allowed? :send-invitations
{:organization-perms only-owners-organization
:profile-id :owner
:team-perms {:is-owner false :is-admin false}})))
(t/is (false? (nitrate-perms/allowed? :send-invitations
{:organization-perms only-owners-organization
:profile-id :member
:team-perms {:is-owner false :is-admin true}})))))
(t/deftest send-invitations-invalid-value-is-denied
(let [invalid-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:send-invitations "invalid-value"})]
(t/is (false? (nitrate-perms/allowed? :send-invitations
{:organization-perms invalid-organization
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))))

View File

@ -0,0 +1,178 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns common-tests.types.organization-test
(:require
[app.common.types.organization :as cto]
[clojure.test :as t]))
(def organization-perms
{:owner-id :owner
:permissions {:create-teams "any"
:delete-teams "onlyOwners"
:send-invitations "ownersAndAdmins"}})
(t/deftest unknown-action-is-denied
(t/is (false? (cto/allowed? :unknown
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-admin true}}))))
(t/deftest organization-owner-is-allowed-for-create-and-delete
(t/is (true? (cto/allowed? :create-team
{:organization-perms organization-perms
:profile-id :owner
:team-perms {:is-admin false}})))
(t/is (true? (cto/allowed? :delete-team
{:organization-perms organization-perms
:profile-id :owner
:team-perms {:is-admin false}}))))
(t/deftest create-team-permission-rules
(t/is (true? (cto/allowed? :create-team
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-admin false}})))
(t/is (false? (cto/allowed? :create-team
{:organization-perms (assoc organization-perms :permissions {:create-teams "none"
:delete-teams "onlyOwners"})
:profile-id :member
:team-perms {:is-admin false}}))))
(t/deftest delete-team-onlyowners-allows-only-team-owners
(t/is (true? (cto/allowed? :delete-team
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))
(t/is (false? (cto/allowed? :delete-team
{:organization-perms organization-perms
:profile-id :member
:team-perms {:is-admin true}})))
(t/is (false? (cto/allowed? :delete-team
{:organization-perms (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "invalid-value"})
:profile-id :member
:team-perms {:is-admin true}}))))
(t/deftest delete-team-onlyme-still-allows-organization-owner
(let [only-me-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyMe"})]
(t/is (true? (cto/allowed? :delete-team
{:organization-perms only-me-organization
:profile-id :owner
:team-perms {:is-owner false :is-admin false}})))
(t/is (false? (cto/allowed? :delete-team
{:organization-perms only-me-organization
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))))
(t/deftest move-team-always-allows-any-organization-owner-or-all-users
(let [always-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "always"})]
;; Organization owner should always be allowed
(t/is (true? (cto/allowed? :move-team
{:organization-perms always-organization
:profile-id :owner
:team-perms {}})))
;; Regular member should be allowed when move-teams is "always"
(t/is (true? (cto/allowed? :move-team
{:organization-perms always-organization
:profile-id :member
:team-perms {}})))))
(t/deftest move-team-myorganizations-allows-only-within-same-owner
(let [my-organizations (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "myOrganizations"})]
;; Organization owner must also stay within same-owner organizations
(t/is (false? (cto/allowed? :move-team
{:organization-perms my-organizations
:profile-id :owner
:team-perms {}
:target-organization-same-owner? false})))
(t/is (true? (cto/allowed? :move-team
{:organization-perms my-organizations
:profile-id :owner
:team-perms {}
:target-organization-same-owner? true})))
;; Regular member should be allowed only if target has same owner
(t/is (true? (cto/allowed? :move-team
{:organization-perms my-organizations
:profile-id :member
:team-perms {}
:target-organization-same-owner? true})))
(t/is (false? (cto/allowed? :move-team
{:organization-perms my-organizations
:profile-id :member
:team-perms {}
:target-organization-same-owner? false})))))
(t/deftest move-team-never-denies-all
(let [never-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:move-teams "never"})]
;; Even organization owner should be denied
(t/is (false? (cto/allowed? :move-team
{:organization-perms never-organization
:profile-id :owner
:team-perms {}})))
;; Regular member should be denied
(t/is (false? (cto/allowed? :move-team
{:organization-perms never-organization
:profile-id :member
:team-perms {}})))))
(t/deftest move-team-defaults-to-always
(let [default-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"})]
;; Should default to "always" when not specified
(t/is (true? (cto/allowed? :move-team
{:organization-perms default-organization
:profile-id :member
:team-perms {}})))))
(t/deftest send-invitations-defaults-to-owners-and-admins
(let [default-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"})]
(t/is (true? (cto/allowed? :send-invitations
{:organization-perms default-organization
:profile-id :owner
:team-perms {:is-owner true :is-admin false}})))
(t/is (true? (cto/allowed? :send-invitations
{:organization-perms default-organization
:profile-id :member
:team-perms {:is-owner false :is-admin true}})))
(t/is (false? (cto/allowed? :send-invitations
{:organization-perms default-organization
:profile-id :member
:team-perms {:is-owner false :is-admin false}})))))
(t/deftest send-invitations-owners-allows-only-team-owners
(let [only-owners-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:send-invitations "owners"})]
(t/is (true? (cto/allowed? :send-invitations
{:organization-perms only-owners-organization
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))
(t/is (false? (cto/allowed? :send-invitations
{:organization-perms only-owners-organization
:profile-id :owner
:team-perms {:is-owner false :is-admin false}})))
(t/is (false? (cto/allowed? :send-invitations
{:organization-perms only-owners-organization
:profile-id :member
:team-perms {:is-owner false :is-admin true}})))))
(t/deftest send-invitations-invalid-value-is-denied
(let [invalid-organization (assoc organization-perms :permissions {:create-teams "any"
:delete-teams "onlyOwners"
:send-invitations "invalid-value"})]
(t/is (false? (cto/allowed? :send-invitations
{:organization-perms invalid-organization
:profile-id :member
:team-perms {:is-owner true :is-admin true}})))))

View File

@ -53,13 +53,13 @@ update_oidc_name /var/www/app/js/config.js
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060} export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061} export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000} export PENPOT_ADMIN_CONSOLE_URI=${PENPOT_ADMIN_CONSOLE_URI:-http://penpot-nitrate:3000}
export PENPOT_HTTP_SERVER_MAX_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_BODY_SIZE:-367001600} # Default to 350MiB export PENPOT_HTTP_SERVER_MAX_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_BODY_SIZE:-367001600} # Default to 350MiB
export PENPOT_IPV6_LISTEN_DIRECTIVE=${PENPOT_IPV6_LISTEN_DIRECTIVE:-"listen [::]:8080 default_server reuseport backlog=16384;"} export PENPOT_IPV6_LISTEN_DIRECTIVE=${PENPOT_IPV6_LISTEN_DIRECTIVE:-"listen [::]:8080 default_server reuseport backlog=16384;"}
if is_truthy "${PENPOT_DISABLE_IPV6_LISTEN:-}"; then if is_truthy "${PENPOT_DISABLE_IPV6_LISTEN:-}"; then
export PENPOT_IPV6_LISTEN_DIRECTIVE="" export PENPOT_IPV6_LISTEN_DIRECTIVE=""
fi fi
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE,\$PENPOT_IPV6_LISTEN_DIRECTIVE" \ envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_ADMIN_CONSOLE_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE,\$PENPOT_IPV6_LISTEN_DIRECTIVE" \
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf < /tmp/nginx.conf.template > /etc/nginx/nginx.conf
if [[ $PENPOT_FLAGS == *"enable-mcp"* ]]; then if [[ $PENPOT_FLAGS == *"enable-mcp"* ]]; then

View File

@ -156,7 +156,7 @@ http {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $http_cf_connecting_ip; proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass $PENPOT_NITRATE_URI$request_uri; proxy_pass $PENPOT_ADMIN_CONSOLE_URI$request_uri;
} }
include /etc/nginx/overrides/server.d/*.conf; include /etc/nginx/overrides/server.d/*.conf;

View File

@ -11,7 +11,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.time :as ct] [app.common.time :as ct]
[app.common.types.organization :as co] [app.common.types.organization :as cto]
[app.common.types.team :as ctt] [app.common.types.team :as ctt]
[app.config :as cf] [app.config :as cf]
[app.main.data.helpers :as dsh] [app.main.data.helpers :as dsh]
@ -255,7 +255,7 @@
(let [current-team-id (:current-team-id state) (let [current-team-id (:current-team-id state)
organization (:organization team) organization (:organization team)
current-team? (= (:id team) current-team-id)] current-team? (= (:id team) current-team-id)]
(when (and (contains? cf/flags :nitrate) (when (and (contains? cf/flags :admin-console)
current-team?) current-team?)
(rx/concat (rx/concat
(when notification (when notification
@ -267,13 +267,13 @@
(check-team-sso current-team-id)))))) (check-team-sso current-team-id))))))
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(let [team-id (:id team) (let [team-id (:id team)
team-name (:name team) team-name (:name team)
organization (:organization team)] organization (:organization team)]
(d/update-in-when state [:teams team-id] (d/update-in-when state [:teams team-id]
(fn [team] (fn [team]
(cond-> (co/apply-organization team organization) (cond-> (cto/apply-organization team organization)
team-name (assoc :name team-name))))) team-name (assoc :name team-name)))))
state)))) state))))
@ -284,7 +284,7 @@
(ptk/reify ::handle-organization-change-sso (ptk/reify ::handle-organization-change-sso
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(let [team-id (:current-team-id state) (let [team-id (:current-team-id state)
team (dm/get-in state [:teams team-id]) team (dm/get-in state [:teams team-id])
team-organization-id (dm/get-in team [:organization :id])] team-organization-id (dm/get-in team [:organization :id])]

View File

@ -690,7 +690,7 @@
(ptk/reify ::handle-user-organization-change (ptk/reify ::handle-user-organization-change
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(when (and notification (contains? cf/flags :nitrate)) (when (and notification (contains? cf/flags :admin-console))
(let [team-id (:current-team-id state) (let [team-id (:current-team-id state)
team (dm/get-in state [:teams team-id])] team (dm/get-in state [:teams team-id])]
(rx/of (ntf/show {:content (tr notification organization-name) (rx/of (ntf/show {:content (tr notification organization-name)
@ -708,7 +708,7 @@
(ptk/reify ::handle-organization-deleted (ptk/reify ::handle-organization-deleted
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
(let [team-id (:current-team-id state) (let [team-id (:current-team-id state)
current-team (dm/get-in state [:teams team-id]) current-team (dm/get-in state [:teams team-id])
current-organization-id (dm/get-in current-team [:organization :id]) current-organization-id (dm/get-in current-team [:organization :id])

View File

@ -1,7 +1,7 @@
(ns app.main.data.nitrate (ns app.main.data.nitrate
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.types.nitrate-permissions :as nitrate-perms] [app.common.types.organization :as cto]
[app.common.uri :as u] [app.common.uri :as u]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
@ -84,9 +84,8 @@
(defn can-send-invitations? (defn can-send-invitations?
[{:keys [organization profile-id team-permissions]}] [{:keys [organization profile-id team-permissions]}]
(nitrate-perms/can-send-invitations? (cto/can-send-invitations?
{:nitrate-enabled? (contains? cf/flags :nitrate) {:organization organization
:organization organization
:profile-id profile-id :profile-id profile-id
:team-permissions team-permissions})) :team-permissions team-permissions}))
@ -149,7 +148,7 @@
(defn is-valid-license? (defn is-valid-license?
[profile] [profile]
(and (contains? cf/flags :nitrate) (and (contains? cf/flags :admin-console)
;; Possible values: "active" "canceled" "incomplete" "incomplete_expired" "past_due" "paused" "trialing" "unpaid" ;; Possible values: "active" "canceled" "incomplete" "incomplete_expired" "past_due" "paused" "trialing" "unpaid"
(contains? #{"active" "past_due" "trialing"} (contains? #{"active" "past_due" "trialing"}
(dm/get-in profile [:subscription :status])))) (dm/get-in profile [:subscription :status]))))
@ -279,7 +278,7 @@
(dt/with-refreshed-team team-id (dt/with-refreshed-team team-id
(fn [team] (fn [team]
(let [source-organization (:organization team) (let [source-organization (:organization team)
can-move? (nitrate-perms/allowed? can-move? (cto/allowed?
:move-team :move-team
{:organization-perms {:owner-id (:owner-id source-organization) {:organization-perms {:owner-id (:owner-id source-organization)
:permissions (:permissions source-organization)} :permissions (:permissions source-organization)}
@ -357,8 +356,8 @@
Organizations where :add-anybody-to-team is permitted are pre-approved; Organizations where :add-anybody-to-team is permitted are pre-approved;
the rest are verified via :all-team-members-in-organizations." the rest are verified via :all-team-members-in-organizations."
[team-id organizations] [team-id organizations]
(let [add-anybody-organizations (filterv #(nitrate-perms/allowed? :add-anybody-to-team {:organization-perms %}) organizations) (let [add-anybody-organizations (filterv #(cto/allowed? :add-anybody-to-team {:organization-perms %}) organizations)
organizations-to-check (filterv #(not (nitrate-perms/allowed? :add-anybody-to-team {:organization-perms %})) organizations) organizations-to-check (filterv #(not (cto/allowed? :add-anybody-to-team {:organization-perms %})) organizations)
organization-ids-to-check (mapv :id organizations-to-check)] organization-ids-to-check (mapv :id organizations-to-check)]
(if (empty? organization-ids-to-check) (if (empty? organization-ids-to-check)
(rx/of (into {} (map (fn [organization] [(:id organization) true])) organizations)) (rx/of (into {} (map (fn [organization] [(:id organization) true])) organizations))

View File

@ -10,7 +10,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.logging :as log] [app.common.logging :as log]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.types.nitrate-permissions :as nitrate-perms] [app.common.types.organization :as cto]
[app.common.types.team :as ctt] [app.common.types.team :as ctt]
[app.common.uri :as u] [app.common.uri :as u]
[app.config :as cf] [app.config :as cf]
@ -42,7 +42,7 @@
default team when the candidate requires SSO and the user has no default team when the candidate requires SSO and the user has no
valid SSO session for it." valid SSO session for it."
[{:keys [team-id default-team-id]}] [{:keys [team-id default-team-id]}]
(if (or (not (contains? cf/flags :nitrate)) (if (or (not (contains? cf/flags :admin-console))
(= team-id default-team-id)) (= team-id default-team-id))
(rx/of team-id) (rx/of team-id)
(->> (rp/cmd! :check-nitrate-sso {:team-id team-id :url (rt/get-current-href)}) (->> (rp/cmd! :check-nitrate-sso {:team-id team-id :url (rt/get-current-href)})
@ -116,13 +116,13 @@
(with-refreshed-team team-id (with-refreshed-team team-id
(fn [team] (fn [team]
(let [organization (:organization team) (let [organization (:organization team)
in-organization? (and (contains? cf/flags :nitrate) organization) in-organization? (and (contains? cf/flags :admin-console) organization)
can-create? (if in-organization? can-create? (if in-organization?
(nitrate-perms/allowed? :create-team (cto/allowed? :create-team
{:organization-perms {:owner-id (:owner-id organization) {:organization-perms {:owner-id (:owner-id organization)
:permissions (:permissions organization)} :permissions (:permissions organization)}
:profile-id profile-id :profile-id profile-id
:team-perms (:permissions team)}) :team-perms (:permissions team)})
true)] true)]
(rx/of (if can-create? (rx/of (if can-create?
(modal/show :team-form (if in-organization? (modal/show :team-form (if in-organization?
@ -142,13 +142,13 @@
(with-refreshed-team team-id (with-refreshed-team team-id
(fn [team] (fn [team]
(let [organization (:organization team) (let [organization (:organization team)
in-organization? (and (contains? cf/flags :nitrate) organization) in-organization? (and (contains? cf/flags :admin-console) organization)
can-delete? (if in-organization? can-delete? (if in-organization?
(nitrate-perms/allowed? :delete-team (cto/allowed? :delete-team
{:organization-perms {:owner-id (:owner-id organization) {:organization-perms {:owner-id (:owner-id organization)
:permissions (:permissions organization)} :permissions (:permissions organization)}
:profile-id profile-id :profile-id profile-id
:team-perms (:permissions team)}) :team-perms (:permissions team)})
(boolean (dm/get-in team [:permissions :is-owner]))) (boolean (dm/get-in team [:permissions :is-owner])))
message (if in-organization? message (if in-organization?
(tr "modals.delete-organization-team-confirm.message" (:name organization)) (tr "modals.delete-organization-team-confirm.message" (:name organization))
@ -173,9 +173,9 @@
:team team :team team
:origin (or origin :team) :origin (or origin :team)
:invite-email invite-email}))] :invite-email invite-email}))]
(if (and (contains? cf/flags :nitrate) (if (and (contains? cf/flags :admin-console)
(not (nitrate-perms/allowed? :add-anybody-to-team (not (cto/allowed? :add-anybody-to-team
{:organization-perms (:organization team)}))) {:organization-perms (:organization team)})))
(->> (rp/cmd! :all-organization-members-in-team (->> (rp/cmd! :all-organization-members-in-team
{:team-id (:id team) {:team-id (:id team)
:organization-id (get-in team [:organization :id])}) :organization-id (get-in team [:organization :id])})
@ -203,9 +203,8 @@
(with-refreshed-team team-id (with-refreshed-team team-id
(fn [team] (fn [team]
(let [organization (:organization team) (let [organization (:organization team)
can-invite? (nitrate-perms/can-send-invitations? can-invite? (cto/can-send-invitations?
{:nitrate-enabled? (contains? cf/flags :nitrate) {:organization organization
:organization organization
:profile-id profile-id :profile-id profile-id
:team-permissions (:permissions team)})] :team-permissions (:permissions team)})]
(rx/of (if can-invite? (rx/of (if can-invite?
@ -615,11 +614,11 @@
(ptk/reify ::check-and-submit-invite-members (ptk/reify ::check-and-submit-invite-members
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(with-refreshed-team team-id (with-refreshed-team team-id
(fn [team] (fn [team]
(if (not (nitrate-perms/allowed? :add-anybody-to-team (if (not (cto/allowed? :add-anybody-to-team
{:organization-perms (:organization team)})) {:organization-perms (:organization team)}))
(->> (rp/cmd! :check-organization-members {:organization-id (get-in team [:organization :id]) (->> (rp/cmd! :check-organization-members {:organization-id (get-in team [:organization :id])
:emails (vec (:emails params))}) :emails (vec (:emails params))})
(rx/mapcat (rx/mapcat

View File

@ -418,7 +418,7 @@
(mf/use-fn (mf/use-fn
(mf/deps team) (mf/deps team)
(fn [] (fn []
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(st/emit! (dtm/check-and-create-team (:id team))) (st/emit! (dtm/check-and-create-team (:id team)))
(st/emit! (modal/show :team-form {}))))) (st/emit! (modal/show :team-form {})))))
@ -434,15 +434,15 @@
[:> dropdown-menu-item* {:on-click on-team-click [:> dropdown-menu-item* {:on-click on-team-click
:data-value default-team-id :data-value default-team-id
:class (stl/css-case :team-dropdown-item true :class (stl/css-case :team-dropdown-item true
:team-dropdown-item-no-logo (contains? cf/flags :nitrate))} :team-dropdown-item-no-logo (contains? cf/flags :admin-console))}
(when-not (contains? cf/flags :nitrate) (when-not (contains? cf/flags :admin-console)
[:span {:class (stl/css :penpot-icon)} deprecated-icon/logo-icon]) [:span {:class (stl/css :penpot-icon)} deprecated-icon/logo-icon])
[:span {:class (stl/css :team-text)} (if (contains? cf/flags :nitrate) (tr "dashboard.my-files") (tr "dashboard.your-penpot"))] [:span {:class (stl/css :team-text)} (if (contains? cf/flags :admin-console) (tr "dashboard.my-files") (tr "dashboard.your-penpot"))]
(when (= default-team-id (:id team)) (when (= default-team-id (:id team))
tick-icon)] tick-icon)]
(when (and (contains? cf/flags :nitrate) (when (and (contains? cf/flags :admin-console)
(seq (remove :is-default (vals teams)))) (seq (remove :is-default (vals teams))))
[:* [:*
[:hr {:role "separator" :class (stl/css :team-separator)}] [:hr {:role "separator" :class (stl/css :team-separator)}]
@ -890,7 +890,7 @@
(mf/defc sidebar-team-switch* (mf/defc sidebar-team-switch*
[{:keys [team profile]}] [{:keys [team profile]}]
(let [nitrate? (contains? cf/flags :nitrate) (let [nitrate? (contains? cf/flags :admin-console)
organization (:organization team) organization (:organization team)
organization-id (when nitrate? (:id organization)) organization-id (when nitrate? (:id organization))
teams (cond->> (mf/deref refs/teams) teams (cond->> (mf/deref refs/teams)
@ -1039,7 +1039,7 @@
overflow* (mf/use-state false) overflow* (mf/use-state false)
overflow? (deref overflow*) overflow? (deref overflow*)
nitrate? (contains? cf/flags :nitrate) nitrate? (contains? cf/flags :admin-console)
focus-timer-ref (use-focus-timer-ref) focus-timer-ref (use-focus-timer-ref)
@ -1401,7 +1401,7 @@
(dom/open-new-window "https://penpot.app/pricing")))] (dom/open-new-window "https://penpot.app/pricing")))]
(mf/with-effect [teams] (mf/with-effect [teams]
(when (and (contains? cf/flags :nitrate) (when (and (contains? cf/flags :admin-console)
(empty? teams)) (empty? teams))
(st/emit! (dtm/fetch-teams)))) (st/emit! (dtm/fetch-teams))))
@ -1410,7 +1410,7 @@
(reset! sub-menu* nil))) (reset! sub-menu* nil)))
[:* [:*
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
[:* [:*
[:> nitrate-sidebar* {:profile profile :teams teams}] [:> nitrate-sidebar* {:profile profile :teams teams}]
[:> nitrate-current-plan* {:profile profile}]] [:> nitrate-current-plan* {:profile profile}]]

View File

@ -1504,7 +1504,7 @@
(mf/defc team-settings-page* (mf/defc team-settings-page*
[{:keys [team]}] [{:keys [team]}]
(let [nitrate? (contains? cfg/flags :nitrate) (let [nitrate? (contains? cfg/flags :admin-console)
finput (mf/use-ref) finput (mf/use-ref)
members (get team :members) members (get team :members)

View File

@ -31,7 +31,7 @@
["/recovery" :auth-recovery] ["/recovery" :auth-recovery]
["/verify-token" :auth-verify-token]] ["/verify-token" :auth-verify-token]]
(when (contains? cf/flags :nitrate) (when (contains? cf/flags :admin-console)
["/subscribe-nitrate" :nitrate-entry]) ["/subscribe-nitrate" :nitrate-entry])
["/settings" ["/settings"
@ -105,7 +105,7 @@
or redirects to the SSO provider URL." or redirects to the SSO provider URL."
[match send-event-info? url] [match send-event-info? url]
(let [route-name (name (get-in match [:data :name])) (let [route-name (name (get-in match [:data :name]))
relevant? (and (contains? cf/flags :nitrate) relevant? (and (contains? cf/flags :admin-console)
(or (str/starts-with? route-name "dashboard") (or (str/starts-with? route-name "dashboard")
(str/starts-with? route-name "workspace"))) (str/starts-with? route-name "workspace")))
team-id-str (when relevant? team-id-str (when relevant?

View File

@ -48,7 +48,7 @@
(with-meta {} {:on-error on-error}))))] (with-meta {} {:on-error on-error}))))]
(mf/with-effect [] (mf/with-effect []
(if (contains? cf/flags :nitrate) (if (contains? cf/flags :admin-console)
(let [sub (->> (rp/cmd! :get-owned-organizations-summary {}) (let [sub (->> (rp/cmd! :get-owned-organizations-summary {})
(rx/subs! (rx/subs!
(fn [result] (reset! organizations* (or result []))) (fn [result] (reset! organizations* (or result [])))

View File

@ -457,7 +457,7 @@
(-> profile :props :subscription) (-> profile :props :subscription)
subscription-type subscription-type
(if (and (contains? cf/flags :nitrate) nitrate?) (:type nitrate-license) (get-subscription-type subscription)) (if (and (contains? cf/flags :admin-console) nitrate?) (:type nitrate-license) (get-subscription-type subscription))
subscription-name subscription-name
(cond (cond
@ -724,7 +724,7 @@
(tr "subscription.settings.professional.teams-editors-benefit") (tr "subscription.settings.professional.teams-editors-benefit")
(tr "subscription.settings.professional.selfhost.community-support"))] (tr "subscription.settings.professional.selfhost.community-support"))]
:cta-text (tr "subscription.settings.subscribe") :cta-text (tr "subscription.settings.subscribe")
:cta-link (if (and (contains? cf/flags :nitrate) nitrate?) :cta-link (if (and (contains? cf/flags :admin-console) nitrate?)
#(open-contact-sales-modal subscription-type "Professional" #(open-contact-sales-modal subscription-type "Professional"
(and (:licenses connectivity) (not (:manual nitrate-license)))) (and (:licenses connectivity) (not (:manual nitrate-license))))
go-to-payments) go-to-payments)
@ -742,14 +742,14 @@
(tr "subscription.settings.unlimited.autosave-benefit"), (tr "subscription.settings.unlimited.autosave-benefit"),
(tr "subscription.settings.unlimited.bill")] (tr "subscription.settings.unlimited.bill")]
:cta-text (if (:type subscription) (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free")) :cta-text (if (:type subscription) (tr "subscription.settings.subscribe") (tr "subscription.settings.try-it-free"))
:cta-link (if (and (contains? cf/flags :nitrate) nitrate?) #(open-contact-sales-modal subscription-type "Unlimited") #(open-subscription-modal "unlimited" subscription)) :cta-link (if (and (contains? cf/flags :admin-console) nitrate?) #(open-contact-sales-modal subscription-type "Unlimited") #(open-subscription-modal "unlimited" subscription))
:cta-text-with-icon (tr "subscription.settings.more-information") :cta-text-with-icon (tr "subscription.settings.more-information")
:cta-link-with-icon go-to-pricing-page :cta-link-with-icon go-to-pricing-page
:recommended (= subscription-type "professional") :recommended (= subscription-type "professional")
:show-button-cta (= subscription-type "professional") :show-button-cta (= subscription-type "professional")
:current-plan false}]) :current-plan false}])
(when (and (not= subscription-type "enterprise") cf/saas? (not (contains? cf/flags :nitrate))) (when (and (not= subscription-type "enterprise") cf/saas? (not (contains? cf/flags :admin-console)))
[:> plan-card* {:card-title (tr "subscription.settings.enterprise") [:> plan-card* {:card-title (tr "subscription.settings.enterprise")
:card-title-icon i/character-e :card-title-icon i/character-e
:price-value "$950" :price-value "$950"
@ -765,7 +765,7 @@
:show-button-cta (= subscription-type "professional") :show-button-cta (= subscription-type "professional")
:current-plan false}]) :current-plan false}])
(when (and (contains? cf/flags :nitrate) (not nitrate?)) (when (and (contains? cf/flags :admin-console) (not nitrate?))
[:> plan-card* {:card-title (tr "subscription.settings.enterprise") [:> plan-card* {:card-title (tr "subscription.settings.enterprise")
:price-value "$25" :price-value "$25"
:price-period (tr "subscription.settings.organization-member-month") :price-period (tr "subscription.settings.organization-member-month")

View File

@ -27,7 +27,7 @@
{:team {:id team-id :organization organization} {:team {:id team-id :organization organization}
:notification nil})] :notification nil})]
(mock/with-mocks (mock/with-mocks
{cf/flags (conj cf/flags :nitrate) {cf/flags (conj cf/flags :admin-console)
rp/cmd! (mock/stub rp/cmd! (mock/stub
(fn [cmd params] (fn [cmd params]
(if (= [:check-nitrate-sso (if (= [:check-nitrate-sso
@ -61,7 +61,7 @@
event (dcm/handle-organization-change-sso event (dcm/handle-organization-change-sso
{:organization-id organization-id})] {:organization-id organization-id})]
(mock/with-mocks (mock/with-mocks
{cf/flags (conj cf/flags :nitrate) {cf/flags (conj cf/flags :admin-console)
rp/cmd! (mock/stub rp/cmd! (mock/stub
(fn [cmd params] (fn [cmd params]
(if (= [:check-nitrate-sso (if (= [:check-nitrate-sso