mirror of
https://github.com/penpot/penpot.git
synced 2026-08-07 13:29:07 +00:00
🐛 Restrict webhook edit/delete to team members only
Remove the creator-id fallback from get-webhooks-permissions. Previously, the webhook creator could always edit/delete their webhook even after being removed from the team. Now can-edit comes from team role only — removed users get :not-found. Webhooks are NOT deleted on member removal; the team owns them and team admins/owners manage them. AI-assisted-by: mimo-v2.5
This commit is contained in:
parent
2a0b75fe09
commit
7ccc52fbdf
@ -23,11 +23,9 @@
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn get-webhooks-permissions
|
||||
[conn profile-id team-id creator-id]
|
||||
[conn profile-id team-id]
|
||||
(let [permissions (t/get-permissions conn profile-id team-id)
|
||||
|
||||
can-edit (boolean (or (:can-edit permissions)
|
||||
(= profile-id creator-id)))]
|
||||
can-edit (boolean (:can-edit permissions))]
|
||||
(assoc permissions :can-edit can-edit)))
|
||||
|
||||
(def has-webhook-edit-permissions?
|
||||
@ -137,7 +135,7 @@
|
||||
::sm/params schema:update-webhook}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(let [whook (-> (db/get pool :webhook {:id id}) (decode-row))]
|
||||
(check-webhook-edition-permissions! pool profile-id (:team-id whook) (:profile-id whook))
|
||||
(check-webhook-edition-permissions! pool profile-id (:team-id whook))
|
||||
(validate-webhook! cfg whook params)
|
||||
(update-webhook! cfg whook params)))
|
||||
|
||||
@ -151,7 +149,7 @@
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn]} {:keys [::rpc/profile-id id]}]
|
||||
(let [whook (-> (db/get conn :webhook {:id id}) decode-row)]
|
||||
(check-webhook-edition-permissions! conn profile-id (:team-id whook) (:profile-id whook))
|
||||
(check-webhook-edition-permissions! conn profile-id (:team-id whook))
|
||||
(db/delete! conn :webhook {:id id})
|
||||
nil))
|
||||
|
||||
|
||||
@ -286,3 +286,91 @@
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type error-data) :restriction))
|
||||
(t/is (= (:code error-data) :webhooks-quote-reached))))))
|
||||
|
||||
(t/deftest removed-user-cannot-edit-webhook
|
||||
(with-mocks [http-mock {:target 'app.http.client/req
|
||||
:return {:status 200}}]
|
||||
|
||||
(let [owner (th/create-profile* 1 {:is-active true})
|
||||
editor (th/create-profile* 2 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id owner)})]
|
||||
|
||||
(th/create-team-role* {:team-id (:id team)
|
||||
:profile-id (:id editor)
|
||||
:role :editor})
|
||||
|
||||
(let [params {::th/type :create-webhook
|
||||
::rpc/profile-id (:id editor)
|
||||
:team-id (:id team)
|
||||
:uri (u/uri "http://example.com")
|
||||
:mtype "application/json"}
|
||||
out (th/command! params)]
|
||||
|
||||
(t/is (nil? (:error out)))
|
||||
(let [whook (:result out)]
|
||||
|
||||
(th/reset-mock! http-mock)
|
||||
|
||||
(t/testing "owner can edit editor's webhook (team owns it)"
|
||||
(let [params {::th/type :update-webhook
|
||||
::rpc/profile-id (:id owner)
|
||||
:id (:id whook)
|
||||
:uri (u/uri "http://example.com/updated")
|
||||
:mtype "application/transit+json"
|
||||
:is-active true}
|
||||
out (th/command! params)]
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= 1 (:call-count @http-mock)))))
|
||||
|
||||
(th/reset-mock! http-mock)
|
||||
|
||||
(t/testing "remove editor from team"
|
||||
(let [params {::th/type :delete-team-member
|
||||
::rpc/profile-id (:id owner)
|
||||
:team-id (:id team)
|
||||
:member-id (:id editor)}
|
||||
out (th/command! params)]
|
||||
(t/is (nil? (:error out)))))
|
||||
|
||||
(th/reset-mock! http-mock)
|
||||
|
||||
(t/testing "removed editor cannot update webhook"
|
||||
(let [params {::th/type :update-webhook
|
||||
::rpc/profile-id (:id editor)
|
||||
:id (:id whook)
|
||||
:uri (u/uri "http://example.com/evil")
|
||||
:mtype "application/transit+json"
|
||||
:is-active true}
|
||||
out (th/command! params)]
|
||||
(t/is (= 0 (:call-count @http-mock)))
|
||||
(let [error (:error out)
|
||||
error-data (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type error-data) :not-found))
|
||||
(t/is (= (:code error-data) :object-not-found)))))
|
||||
|
||||
(th/reset-mock! http-mock)
|
||||
|
||||
(t/testing "removed editor cannot delete webhook"
|
||||
(let [params {::th/type :delete-webhook
|
||||
::rpc/profile-id (:id editor)
|
||||
:id (:id whook)}
|
||||
out (th/command! params)]
|
||||
(t/is (= 0 (:call-count @http-mock)))
|
||||
(let [error (:error out)
|
||||
error-data (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
(t/is (= (:type error-data) :not-found))
|
||||
(t/is (= (:code error-data) :object-not-found)))))
|
||||
|
||||
(th/reset-mock! http-mock)
|
||||
|
||||
(t/testing "owner can still delete editor's webhook"
|
||||
(let [params {::th/type :delete-webhook
|
||||
::rpc/profile-id (:id owner)
|
||||
:id (:id whook)}
|
||||
out (th/command! params)]
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (nil? (:result out)))
|
||||
(let [rows (th/db-exec! ["select * from webhook"])]
|
||||
(t/is (= 0 (count rows)))))))))))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user