mirror of
https://github.com/penpot/penpot.git
synced 2026-09-11 14:39:35 +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}))))
|
:organizations organizations}))))
|
||||||
nil)
|
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
|
(def ^:private sql:get-profile-emails-by-ids
|
||||||
"SELECT email
|
"SELECT email
|
||||||
@ -718,35 +719,68 @@ LEFT JOIN profile AS p
|
|||||||
WHERE id = ANY(?)
|
WHERE id = ANY(?)
|
||||||
AND deleted_at IS NULL")
|
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
|
"DELETE FROM team_invitation
|
||||||
WHERE team_id = ANY(?)
|
WHERE team_id = ANY(?)
|
||||||
AND email_to <> ALL(?)
|
AND email_to <> ALL(?)
|
||||||
RETURNING email_to")
|
RETURNING email_to")
|
||||||
|
|
||||||
(def ^:private schema:cleanup-org-team-invitations-params
|
(def ^:private schema:org-team-invitations-for-non-members-params
|
||||||
[:map
|
[:map
|
||||||
[:team-ids [:vector ::sm/uuid]]
|
[:team-ids [:vector ::sm/uuid]]
|
||||||
[:member-ids [:vector ::sm/uuid]]])
|
[:member-ids [:vector ::sm/uuid]]])
|
||||||
|
|
||||||
(sv/defmethod ::cleanup-org-team-invitations
|
(def ^:private schema:exists-org-team-invitations-for-non-members-result
|
||||||
"Delete team invitations for emails that are not organization members"
|
[: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"
|
{::doc/added "2.18"
|
||||||
::sm/params schema:cleanup-org-team-invitations-params
|
::sm/params schema:org-team-invitations-for-non-members-params
|
||||||
::db/transaction true}
|
::sm/result schema:exists-org-team-invitations-for-non-members-result}
|
||||||
[cfg {:keys [team-ids member-ids]}]
|
[cfg params]
|
||||||
(db/run! cfg (fn [{:keys [::db/conn]}]
|
(db/run! cfg (fn [{:keys [::db/conn]}]
|
||||||
(let [;; Get emails of organization members
|
{:exists (boolean (non-member-org-team-invitations-exist? conn params))})))
|
||||||
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))
|
(sv/defmethod ::delete-org-team-invitations-for-non-members
|
||||||
teams-array (db/create-array conn "uuid" team-ids)]
|
"Delete team invitations for emails that are not organization members."
|
||||||
|
{::doc/added "2.18"
|
||||||
;; Delete invitations that are not in the keep list
|
::sm/params schema:org-team-invitations-for-non-members-params
|
||||||
(db/exec! conn [sql:delete-orphaned-team-invitations teams-array emails-array])
|
::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))))
|
nil))))
|
||||||
|
|
||||||
;; ---- API: push-audit-events
|
;; ---- API: push-audit-events
|
||||||
|
|||||||
@ -684,14 +684,72 @@
|
|||||||
(t/is (nil? (:result out)))
|
(t/is (nil? (:result out)))
|
||||||
(t/is (empty? remaining)))))
|
(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"})
|
(let [member1 (th/create-profile* 1 {:is-active true :email "member1@example.com"})
|
||||||
profile (th/create-profile* 4 {:is-active true})
|
profile (th/create-profile* 4 {:is-active true})
|
||||||
team-1 (th/create-team* 1 {:profile-id (:id profile)})
|
team-1 (th/create-team* 1 {:profile-id (:id profile)})
|
||||||
team-2 (th/create-team* 2 {:profile-id (:id profile)})
|
team-2 (th/create-team* 2 {:profile-id (:id profile)})
|
||||||
outside-team (th/create-team* 3 {:profile-id (:id profile)})
|
outside-team (th/create-team* 3 {:profile-id (:id profile)})
|
||||||
org-id (uuid/random)
|
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)
|
::rpc/profile-id (:id profile)
|
||||||
:organization-id org-id
|
:organization-id org-id
|
||||||
:team-ids [(:id team-1) (:id team-2)]
|
:team-ids [(:id team-1) (:id team-2)]
|
||||||
|
|||||||
@ -973,7 +973,7 @@
|
|||||||
.modal-select-org-body {
|
.modal-select-org-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--sp-xxl);
|
gap: var(--sp-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-select-org-warning {
|
.modal-select-org-warning {
|
||||||
@ -998,7 +998,6 @@
|
|||||||
@include t.use-typography("headline-large");
|
@include t.use-typography("headline-large");
|
||||||
|
|
||||||
color: var(--color-foreground-primary);
|
color: var(--color-foreground-primary);
|
||||||
height: $sz-40;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-select-org-text {
|
.modal-select-org-text {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user