mirror of
https://github.com/penpot/penpot.git
synced 2026-06-09 08:52:05 +00:00
✨ Review nitrate add team members permission
This commit is contained in:
parent
03c02d5adf
commit
2a48747cf6
@ -710,7 +710,8 @@ LEFT JOIN profile AS p
|
||||
:organizations organizations}))))
|
||||
nil)
|
||||
|
||||
;; API: cleanup-org-team-invitations
|
||||
;; API: exists-org-team-invitations-for-non-members /
|
||||
;; delete-org-team-invitations-for-non-members
|
||||
|
||||
(def ^:private sql:get-profile-emails-by-ids
|
||||
"SELECT email
|
||||
@ -718,35 +719,68 @@ LEFT JOIN profile AS p
|
||||
WHERE id = ANY(?)
|
||||
AND deleted_at IS NULL")
|
||||
|
||||
(def ^:private sql:delete-orphaned-team-invitations
|
||||
(def ^:private sql:exists-non-member-org-team-invitations
|
||||
"SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM team_invitation
|
||||
WHERE team_id = ANY(?)
|
||||
AND email_to <> ALL(?)
|
||||
) AS non_member")
|
||||
|
||||
(def ^:private sql:delete-non-member-org-team-invitations
|
||||
"DELETE FROM team_invitation
|
||||
WHERE team_id = ANY(?)
|
||||
AND email_to <> ALL(?)
|
||||
RETURNING email_to")
|
||||
|
||||
(def ^:private schema:cleanup-org-team-invitations-params
|
||||
(def ^:private schema:org-team-invitations-for-non-members-params
|
||||
[:map
|
||||
[:team-ids [:vector ::sm/uuid]]
|
||||
[:member-ids [:vector ::sm/uuid]]])
|
||||
|
||||
(sv/defmethod ::cleanup-org-team-invitations
|
||||
"Delete team invitations for emails that are not organization members"
|
||||
(def ^:private schema:exists-org-team-invitations-for-non-members-result
|
||||
[:map [:exists ::sm/boolean]])
|
||||
|
||||
(defn- org-team-invitations-for-non-members-arrays
|
||||
"Member emails and PG arrays used by exists/delete org team invitation endpoints."
|
||||
[conn {:keys [team-ids member-ids]}]
|
||||
(let [member-ids-array (db/create-array conn "uuid" member-ids)
|
||||
member-emails (->> (db/exec! conn [sql:get-profile-emails-by-ids member-ids-array])
|
||||
(map :email)
|
||||
(into #{}))]
|
||||
{:emails-array (db/create-array conn "text" (vec member-emails))
|
||||
:teams-array (db/create-array conn "uuid" team-ids)}))
|
||||
|
||||
(defn- non-member-org-team-invitations-exist?
|
||||
[conn params]
|
||||
(let [{:keys [emails-array teams-array]}
|
||||
(org-team-invitations-for-non-members-arrays conn params)]
|
||||
(-> (db/exec-one! conn [sql:exists-non-member-org-team-invitations
|
||||
teams-array
|
||||
emails-array])
|
||||
:non-member)))
|
||||
|
||||
(sv/defmethod ::exists-org-team-invitations-for-non-members
|
||||
"Return if there are any team invitations for emails that are not organization members."
|
||||
{::doc/added "2.18"
|
||||
::sm/params schema:cleanup-org-team-invitations-params
|
||||
::db/transaction true}
|
||||
[cfg {:keys [team-ids member-ids]}]
|
||||
::sm/params schema:org-team-invitations-for-non-members-params
|
||||
::sm/result schema:exists-org-team-invitations-for-non-members-result}
|
||||
[cfg params]
|
||||
(db/run! cfg (fn [{:keys [::db/conn]}]
|
||||
(let [;; Get emails of organization members
|
||||
member-ids-array (db/create-array conn "uuid" member-ids)
|
||||
member-emails (->> (db/exec! conn [sql:get-profile-emails-by-ids member-ids-array])
|
||||
(map :email)
|
||||
(into #{}))
|
||||
{:exists (boolean (non-member-org-team-invitations-exist? conn params))})))
|
||||
|
||||
emails-array (db/create-array conn "text" (vec member-emails))
|
||||
teams-array (db/create-array conn "uuid" team-ids)]
|
||||
|
||||
;; Delete invitations that are not in the keep list
|
||||
(db/exec! conn [sql:delete-orphaned-team-invitations teams-array emails-array])
|
||||
(sv/defmethod ::delete-org-team-invitations-for-non-members
|
||||
"Delete team invitations for emails that are not organization members."
|
||||
{::doc/added "2.18"
|
||||
::sm/params schema:org-team-invitations-for-non-members-params
|
||||
::db/transaction true}
|
||||
[cfg params]
|
||||
(db/run! cfg (fn [{:keys [::db/conn]}]
|
||||
(let [{:keys [emails-array teams-array]}
|
||||
(org-team-invitations-for-non-members-arrays conn params)]
|
||||
(db/exec! conn [sql:delete-non-member-org-team-invitations
|
||||
teams-array
|
||||
emails-array])
|
||||
nil))))
|
||||
|
||||
;; ---- API: push-audit-events
|
||||
|
||||
@ -684,14 +684,72 @@
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (empty? remaining)))))
|
||||
|
||||
(t/deftest cleanup-org-team-invitations-removes-orphaned-invitations
|
||||
(t/deftest exists-org-team-invitations-for-non-members-reports-invitations-to-delete
|
||||
(let [member1 (th/create-profile* 1 {:is-active true :email "member1@example.com"})
|
||||
profile (th/create-profile* 4 {:is-active true})
|
||||
team-1 (th/create-team* 1 {:profile-id (:id profile)})
|
||||
team-2 (th/create-team* 2 {:profile-id (:id profile)})
|
||||
outside-team (th/create-team* 3 {:profile-id (:id profile)})
|
||||
org-id (uuid/random)
|
||||
base-params {::th/type :exists-org-team-invitations-for-non-members
|
||||
::rpc/profile-id (:id profile)
|
||||
:organization-id org-id
|
||||
:team-ids [(:id team-1) (:id team-2)]
|
||||
:member-ids [(:id member1)]}
|
||||
exist! (fn [] (-> (management-command-with-nitrate! base-params)
|
||||
:result
|
||||
:exists))]
|
||||
|
||||
(t/is (false? (exist!)))
|
||||
|
||||
(th/db-insert! :team-invitation
|
||||
{:id (uuid/random)
|
||||
:team-id (:id team-1)
|
||||
:org-id nil
|
||||
:email-to "member1@example.com"
|
||||
:created-by (:id profile)
|
||||
:role "editor"
|
||||
:valid-until (ct/in-future "24h")})
|
||||
(t/is (false? (exist!)))
|
||||
|
||||
(th/db-insert! :team-invitation
|
||||
{:id (uuid/random)
|
||||
:org-id org-id
|
||||
:team-id nil
|
||||
:email-to "pending@example.com"
|
||||
:created-by (:id profile)
|
||||
:role "editor"
|
||||
:valid-until (ct/in-future "24h")})
|
||||
(t/is (false? (exist!)))
|
||||
|
||||
(th/db-insert! :team-invitation
|
||||
{:id (uuid/random)
|
||||
:team-id (:id outside-team)
|
||||
:org-id nil
|
||||
:email-to "outsider@example.com"
|
||||
:created-by (:id profile)
|
||||
:role "editor"
|
||||
:valid-until (ct/in-future "24h")})
|
||||
(t/is (false? (exist!)))
|
||||
|
||||
(th/db-insert! :team-invitation
|
||||
{:id (uuid/random)
|
||||
:team-id (:id team-2)
|
||||
:org-id nil
|
||||
:email-to "orphan@example.com"
|
||||
:created-by (:id profile)
|
||||
:role "editor"
|
||||
:valid-until (ct/in-future "24h")})
|
||||
(t/is (true? (exist!)))))
|
||||
|
||||
(t/deftest delete-org-team-invitations-for-non-members-removes-non-member-invitations
|
||||
(let [member1 (th/create-profile* 1 {:is-active true :email "member1@example.com"})
|
||||
profile (th/create-profile* 4 {:is-active true})
|
||||
team-1 (th/create-team* 1 {:profile-id (:id profile)})
|
||||
team-2 (th/create-team* 2 {:profile-id (:id profile)})
|
||||
outside-team (th/create-team* 3 {:profile-id (:id profile)})
|
||||
org-id (uuid/random)
|
||||
params {::th/type :cleanup-org-team-invitations
|
||||
params {::th/type :delete-org-team-invitations-for-non-members
|
||||
::rpc/profile-id (:id profile)
|
||||
:organization-id org-id
|
||||
:team-ids [(:id team-1) (:id team-2)]
|
||||
|
||||
@ -973,7 +973,7 @@
|
||||
.modal-select-org-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xxl);
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.modal-select-org-warning {
|
||||
@ -998,7 +998,6 @@
|
||||
@include t.use-typography("headline-large");
|
||||
|
||||
color: var(--color-foreground-primary);
|
||||
height: $sz-40;
|
||||
}
|
||||
|
||||
.modal-select-org-text {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user