mirror of
https://github.com/penpot/penpot.git
synced 2026-09-10 22:19:19 +00:00
🐛 Add missing permission checks to plugin API (tokens, shapes, variants, flows, layouts, user identity) (#11139)
* 🐛 Add content:write permission checks to Design Tokens plugin API The Design Tokens API (tokens.cljs) had zero permission checks, allowing any plugin to create, modify, and delete tokens, sets, and themes regardless of granted permissions. Add r/check-permission checks to all 22 write operations across: - token-proxy: name, value, description, duplicate, remove, applyToken - token-set-proxy: name, active, toggleActive, addToken, duplicate, remove - token-theme-proxy: group, name, active, toggleActive, addSet, removeSet, duplicate, remove - tokens-catalog: addTheme, addSet Follows the established pattern from comments.cljs, file.cljs, page.cljs. Closes #11137 AI-assisted-by: qwen3.7-plus * 🐛 Add permission checks to shape proxy interactions, detach, export, and variants The shape proxy (shape.cljs) had multiple operations missing permission checks, plus a cond ordering bug that bypassed the existing content:write check for text shapes in commit-fills!. Fix commit-fills! cond ordering: move permission check before the text-shape branch so text shapes are also protected. Add content:write permission checks to: - interaction-proxy: :trigger, :delay, :action setters, :remove method - shape-proxy: :addInteraction, :removeInteraction, :detach - shape-proxy: :applyToken, :switchVariant, :combineAsVariants Add content:read permission check to: - shape-proxy: :export (read/extraction operation) Follows the established pattern from :resize, :rotate, :blocked setters. Relates to #11137 AI-assisted-by: qwen3.7-plus * 🐛 Add library:write permission checks to variant plugin API The library.cljs variant operations (variant-proxy and lib-component-proxy) had seven mutating operations that did not check the library:write permission, allowing any plugin to create, modify, and delete component variants regardless of granted permissions. Add r/check-permission checks to all 7 operations: - variant-proxy: addVariant, addProperty, removeProperty, renameProperty - lib-component-proxy: transformInVariant, addVariant, setVariantProperty Follows the established pattern from the :name and :path setters in the same file. Relates to #11137 AI-assisted-by: qwen3.7-plus * 🐛 Add content:write permission checks to flow and flex layout plugin API Add permission checks to prototype flow and flex layout operations that were missing them, allowing plugins to modify flows and layout structure without explicit user permission. Changes: - page.cljs: Add content:write checks to flow-proxy (name, startingBoard setters, remove) and page-proxy (createFlow, removeFlow) - flex.cljs: Add content:write checks to flex-layout-proxy (remove, appendChild) Follows the established pattern from tokens.cljs, shape.cljs, and library.cljs. Relates to #11137 AI-assisted-by: qwen3.7-plus * 🐛 Add user:read permission checks to plugin API Add permission checks to user identity accessors that were bypassing the consent model, allowing plugins to access user data regardless of whether the user granted user:read permission. Changes: - api.cljs: Add user:read checks to getCurrentUser and getActiveUsers - comments.cljs: Add user:read checks to comment-proxy and comment-thread-proxy owner/user getters - file.cljs: Add user:read check to file-version-proxy createdBy getter When user:read permission is not granted: - getCurrentUser() returns null - getActiveUsers() returns empty array - owner/user/createdBy getters return null Follows the established pattern from other permission checks in the plugin API. Relates to #11137 AI-assisted-by: qwen3.7-plus * 🐛 Fix problem with token API --------- Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
This commit is contained in:
parent
5f1e151e84
commit
937b3fc65f
@ -47,6 +47,7 @@
|
|||||||
[app.plugins.page :as page]
|
[app.plugins.page :as page]
|
||||||
[app.plugins.parser :as parser]
|
[app.plugins.parser :as parser]
|
||||||
[app.plugins.reflow :as wrfp]
|
[app.plugins.reflow :as wrfp]
|
||||||
|
[app.plugins.register :as r]
|
||||||
[app.plugins.shape :as shape]
|
[app.plugins.shape :as shape]
|
||||||
[app.plugins.system-events :as se]
|
[app.plugins.system-events :as se]
|
||||||
[app.plugins.user :as user]
|
[app.plugins.user :as user]
|
||||||
@ -242,15 +243,18 @@
|
|||||||
|
|
||||||
:getCurrentUser
|
:getCurrentUser
|
||||||
(fn []
|
(fn []
|
||||||
(user/current-user-proxy plugin-id (:session-id @st/state)))
|
(when (r/check-permission plugin-id "user:read")
|
||||||
|
(user/current-user-proxy plugin-id (:session-id @st/state))))
|
||||||
|
|
||||||
:getActiveUsers
|
:getActiveUsers
|
||||||
(fn []
|
(fn []
|
||||||
(apply array
|
(if (r/check-permission plugin-id "user:read")
|
||||||
(->> (:workspace-presence @st/state)
|
(apply array
|
||||||
(vals)
|
(->> (:workspace-presence @st/state)
|
||||||
(remove #(= (:id %) (:session-id @st/state)))
|
(vals)
|
||||||
(map #(user/active-user-proxy plugin-id (:id %))))))
|
(remove #(= (:id %) (:session-id @st/state)))
|
||||||
|
(map #(user/active-user-proxy plugin-id (:id %)))))
|
||||||
|
(array)))
|
||||||
|
|
||||||
:uploadMediaUrl
|
:uploadMediaUrl
|
||||||
(fn [name url]
|
(fn [name url]
|
||||||
|
|||||||
@ -40,12 +40,14 @@
|
|||||||
|
|
||||||
;; FIXME: inconsistent with comment-thread: owner
|
;; FIXME: inconsistent with comment-thread: owner
|
||||||
:user
|
:user
|
||||||
{:get #(->> (dc/get-owner data)
|
{:get #(when (r/check-permission plugin-id "user:read")
|
||||||
(user/user-proxy plugin-id))}
|
(->> (dc/get-owner data)
|
||||||
|
(user/user-proxy plugin-id)))}
|
||||||
|
|
||||||
:owner
|
:owner
|
||||||
{:get #(->> (dc/get-owner data)
|
{:get #(when (r/check-permission plugin-id "user:read")
|
||||||
(user/user-proxy plugin-id))}
|
(->> (dc/get-owner data)
|
||||||
|
(user/user-proxy plugin-id)))}
|
||||||
|
|
||||||
:date
|
:date
|
||||||
{:get
|
{:get
|
||||||
@ -116,8 +118,9 @@
|
|||||||
:board {:get #(shape/shape-proxy plugin-id file-id page-id (:frame-id data))}
|
:board {:get #(shape/shape-proxy plugin-id file-id page-id (:frame-id data))}
|
||||||
|
|
||||||
:owner
|
:owner
|
||||||
{:get #(->> (dc/get-owner data)
|
{:get #(when (r/check-permission plugin-id "user:read")
|
||||||
(user/user-proxy plugin-id))}
|
(->> (dc/get-owner data)
|
||||||
|
(user/user-proxy plugin-id)))}
|
||||||
|
|
||||||
:position
|
:position
|
||||||
{:get
|
{:get
|
||||||
|
|||||||
@ -61,8 +61,9 @@
|
|||||||
:createdBy
|
:createdBy
|
||||||
{:get
|
{:get
|
||||||
(fn []
|
(fn []
|
||||||
(when-let [user-data (get users (:profile-id @data))]
|
(when (r/check-permission plugin-id "user:read")
|
||||||
(user/user-proxy plugin-id user-data)))}
|
(when-let [user-data (get users (:profile-id @data))]
|
||||||
|
(user/user-proxy plugin-id user-data))))}
|
||||||
|
|
||||||
:createdAt
|
:createdAt
|
||||||
{:get #(:created-at @data)}
|
{:get #(:created-at @data)}
|
||||||
|
|||||||
@ -325,7 +325,12 @@
|
|||||||
|
|
||||||
:remove
|
:remove
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwsl/remove-layout #{id})))
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwsl/remove-layout #{id}))))
|
||||||
|
|
||||||
:appendChild
|
:appendChild
|
||||||
(fn [child]
|
(fn [child]
|
||||||
@ -350,6 +355,9 @@
|
|||||||
(u/changes-component-copy-structure? objects shape child-shape)
|
(u/changes-component-copy-structure? objects shape child-shape)
|
||||||
(u/not-valid plugin-id :appendChild "Cannot change the structure of a component copy")
|
(u/not-valid plugin-id :appendChild "Cannot change the structure of a component copy")
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :appendChild "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(dwsh/relocate-shapes #{child-id} id index)
|
(dwsh/relocate-shapes #{child-id} id index)
|
||||||
|
|||||||
@ -698,21 +698,37 @@
|
|||||||
|
|
||||||
:addVariant
|
:addVariant
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit!
|
(cond
|
||||||
(se/event plugin-id "add-new-variant")
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
(dwv/add-new-variant id)))
|
(u/not-valid plugin-id :addVariant "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit!
|
||||||
|
(se/event plugin-id "add-new-variant")
|
||||||
|
(dwv/add-new-variant id))))
|
||||||
|
|
||||||
:addProperty
|
:addProperty
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit!
|
(cond
|
||||||
(se/event plugin-id "add-new-property")
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
(dwv/add-new-property id {:property-value "Value 1"})))
|
(u/not-valid plugin-id :addProperty "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit!
|
||||||
|
(se/event plugin-id "add-new-property")
|
||||||
|
(dwv/add-new-property id {:property-value "Value 1"}))))
|
||||||
|
|
||||||
:removeProperty
|
:removeProperty
|
||||||
(fn [pos]
|
(fn [pos]
|
||||||
(let [nprops (->> (get-variant-components file-id id) first :variant-properties count)]
|
(let [nprops (->> (get-variant-components file-id id) first :variant-properties count)]
|
||||||
(if (or (not (nat-int? pos)) (>= pos nprops))
|
(cond
|
||||||
|
(or (not (nat-int? pos)) (>= pos nprops))
|
||||||
(u/not-valid plugin-id :pos pos)
|
(u/not-valid plugin-id :pos pos)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
|
(u/not-valid plugin-id :removeProperty "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(se/event plugin-id "remove-property")
|
(se/event plugin-id "remove-property")
|
||||||
(dwv/remove-property id pos)))))
|
(dwv/remove-property id pos)))))
|
||||||
@ -727,6 +743,9 @@
|
|||||||
(not (string? name))
|
(not (string? name))
|
||||||
(u/not-valid plugin-id :name name)
|
(u/not-valid plugin-id :name name)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
|
(u/not-valid plugin-id :renameProperty "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(dwv/update-property-name id pos name {:trigger "plugin:rename-property"})))))))
|
(dwv/update-property-name id pos name {:trigger "plugin:rename-property"})))))))
|
||||||
@ -923,8 +942,15 @@
|
|||||||
:transformInVariant
|
:transformInVariant
|
||||||
(fn []
|
(fn []
|
||||||
(let [component (u/locate-library-component file-id id)]
|
(let [component (u/locate-library-component file-id id)]
|
||||||
(when (and component
|
(cond
|
||||||
(not (ctk/is-variant? component)))
|
(or (nil? component)
|
||||||
|
(ctk/is-variant? component))
|
||||||
|
nil
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
|
(u/not-valid plugin-id :transformInVariant "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(se/event plugin-id "transform-in-variant")
|
(se/event plugin-id "transform-in-variant")
|
||||||
(dwv/transform-in-variant (:main-instance-id component))))))
|
(dwv/transform-in-variant (:main-instance-id component))))))
|
||||||
@ -932,8 +958,15 @@
|
|||||||
:addVariant
|
:addVariant
|
||||||
(fn []
|
(fn []
|
||||||
(let [component (u/locate-library-component file-id id)]
|
(let [component (u/locate-library-component file-id id)]
|
||||||
(when (and component
|
(cond
|
||||||
(ctk/is-variant? component))
|
(or (nil? component)
|
||||||
|
(not (ctk/is-variant? component)))
|
||||||
|
nil
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
|
(u/not-valid plugin-id :addVariant "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(se/event plugin-id "add-new-variant")
|
(se/event plugin-id "add-new-variant")
|
||||||
(dwv/add-new-variant (:main-instance-id component))))))
|
(dwv/add-new-variant (:main-instance-id component))))))
|
||||||
@ -948,6 +981,9 @@
|
|||||||
(not (string? value))
|
(not (string? value))
|
||||||
(u/not-valid plugin-id :name value)
|
(u/not-valid plugin-id :name value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "library:write"))
|
||||||
|
(u/not-valid plugin-id :setVariantProperty "Plugin doesn't have 'library:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(se/event plugin-id "variant-edit-property-value")
|
(se/event plugin-id "variant-edit-property-value")
|
||||||
|
|||||||
@ -64,6 +64,9 @@
|
|||||||
(or (not (string? value)) (empty? value))
|
(or (not (string? value)) (empty? value))
|
||||||
(u/not-valid plugin-id :name value)
|
(u/not-valid plugin-id :name value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
|
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
|
||||||
|
|
||||||
@ -79,12 +82,20 @@
|
|||||||
(not (shape/shape-proxy? value))
|
(not (shape/shape-proxy? value))
|
||||||
(u/not-valid plugin-id :startingBoard value)
|
(u/not-valid plugin-id :startingBoard value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :startingBoard "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}
|
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}
|
||||||
|
|
||||||
:remove
|
:remove
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwi/remove-flow page-id id)))))
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwi/remove-flow page-id id))))))
|
||||||
|
|
||||||
(defn page-proxy? [proxy]
|
(defn page-proxy? [proxy]
|
||||||
(obj/type-of? proxy "PageProxy"))
|
(obj/type-of? proxy "PageProxy"))
|
||||||
@ -315,6 +326,9 @@
|
|||||||
(not (shape/shape-proxy? frame))
|
(not (shape/shape-proxy? frame))
|
||||||
(u/not-valid plugin-id :createFlow-frame frame)
|
(u/not-valid plugin-id :createFlow-frame frame)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :createFlow "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [flow-id (uuid/next)]
|
(let [flow-id (uuid/next)]
|
||||||
(st/emit!
|
(st/emit!
|
||||||
@ -328,6 +342,9 @@
|
|||||||
(not (flow-proxy? flow))
|
(not (flow-proxy? flow))
|
||||||
(u/not-valid plugin-id :removeFlow-flow flow)
|
(u/not-valid plugin-id :removeFlow-flow flow)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :removeFlow "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(dwi/remove-flow id (obj/get flow "$id"))
|
(dwi/remove-flow id (obj/get flow "$id"))
|
||||||
|
|||||||
@ -103,6 +103,9 @@
|
|||||||
(not (contains? ctsi/event-types value))
|
(not (contains? ctsi/event-types value))
|
||||||
(u/not-valid plugin-id :trigger value)
|
(u/not-valid plugin-id :trigger value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :trigger "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dwi/update-interaction
|
(st/emit! (dwi/update-interaction
|
||||||
(u/locate-shape file-id page-id shape-id)
|
(u/locate-shape file-id page-id shape-id)
|
||||||
@ -119,6 +122,9 @@
|
|||||||
(or (not (sm/valid-safe-int? value)) (neg? value))
|
(or (not (sm/valid-safe-int? value)) (neg? value))
|
||||||
(u/not-valid plugin-id :delay value)
|
(u/not-valid plugin-id :delay value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :delay "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dwi/update-interaction
|
(st/emit! (dwi/update-interaction
|
||||||
(u/locate-shape file-id page-id shape-id)
|
(u/locate-shape file-id page-id shape-id)
|
||||||
@ -139,6 +145,9 @@
|
|||||||
(not (sm/validate ctsi/schema:interaction interaction))
|
(not (sm/validate ctsi/schema:interaction interaction))
|
||||||
(u/not-valid plugin-id :action interaction)
|
(u/not-valid plugin-id :action interaction)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :action "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dwi/update-interaction
|
(st/emit! (dwi/update-interaction
|
||||||
(u/locate-shape file-id page-id shape-id)
|
(u/locate-shape file-id page-id shape-id)
|
||||||
@ -148,7 +157,12 @@
|
|||||||
|
|
||||||
:remove
|
:remove
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwi/remove-interaction {:id shape-id} index)))))
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwi/remove-interaction {:id shape-id} index))))))
|
||||||
|
|
||||||
(def lib-typography-proxy? nil)
|
(def lib-typography-proxy? nil)
|
||||||
(def lib-component-proxy nil)
|
(def lib-component-proxy nil)
|
||||||
@ -200,15 +214,15 @@
|
|||||||
(not (sm/validate [:vector types.fills/schema:fill] value))
|
(not (sm/validate [:vector types.fills/schema:fill] value))
|
||||||
(u/not-valid plugin-id :fills value)
|
(u/not-valid plugin-id :fills value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :fills "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
(not (u/page-active? (obj/get self "$page")))
|
(not (u/page-active? (obj/get self "$page")))
|
||||||
(u/not-valid plugin-id :fills "Cannot modify a page that is not currently active")
|
(u/not-valid plugin-id :fills "Cannot modify a page that is not currently active")
|
||||||
|
|
||||||
(cfh/text-shape? shape)
|
(cfh/text-shape? shape)
|
||||||
(st/emit! (dwt/update-attrs id {:fills value}))
|
(st/emit! (dwt/update-attrs id {:fills value}))
|
||||||
|
|
||||||
(not (r/check-permission plugin-id "content:write"))
|
|
||||||
(u/not-valid plugin-id :fills "Plugin doesn't have 'content:write' permission")
|
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :fills value))))))
|
(st/emit! (dwsh/update-shapes [id] #(assoc % :fills value))))))
|
||||||
|
|
||||||
@ -1475,6 +1489,9 @@
|
|||||||
:detach
|
:detach
|
||||||
(fn []
|
(fn []
|
||||||
(cond
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :detach "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
(not (u/page-active? page-id))
|
(not (u/page-active? page-id))
|
||||||
(u/not-valid plugin-id :detach "Cannot modify a page that is not currently active")
|
(u/not-valid plugin-id :detach "Cannot modify a page that is not currently active")
|
||||||
|
|
||||||
@ -1485,12 +1502,12 @@
|
|||||||
(fn [component]
|
(fn [component]
|
||||||
(let [shape (u/locate-shape file-id page-id id)]
|
(let [shape (u/locate-shape file-id page-id id)]
|
||||||
(cond
|
(cond
|
||||||
(not (u/page-active? page-id))
|
|
||||||
(u/not-valid plugin-id :swapComponent "Cannot modify a page that is not currently active")
|
|
||||||
|
|
||||||
(not (r/check-permission plugin-id "content:write"))
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(u/not-valid plugin-id :swapComponent "Plugin doesn't have 'content:write' permission")
|
(u/not-valid plugin-id :swapComponent "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
(not (u/page-active? page-id))
|
||||||
|
(u/not-valid plugin-id :swapComponent "Cannot modify a page that is not currently active")
|
||||||
|
|
||||||
(not (obj/type-of? component "LibraryComponentProxy"))
|
(not (obj/type-of? component "LibraryComponentProxy"))
|
||||||
(u/not-valid plugin-id :swapComponent "Component not valid")
|
(u/not-valid plugin-id :swapComponent "Component not valid")
|
||||||
|
|
||||||
@ -1507,12 +1524,12 @@
|
|||||||
(fn []
|
(fn []
|
||||||
(let [shape (u/locate-shape file-id page-id id)]
|
(let [shape (u/locate-shape file-id page-id id)]
|
||||||
(cond
|
(cond
|
||||||
(not (u/page-active? page-id))
|
|
||||||
(u/not-valid plugin-id :resetOverrides "Cannot modify a page that is not currently active")
|
|
||||||
|
|
||||||
(not (r/check-permission plugin-id "content:write"))
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(u/not-valid plugin-id :resetOverrides "Plugin doesn't have 'content:write' permission")
|
(u/not-valid plugin-id :resetOverrides "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
(not (u/page-active? page-id))
|
||||||
|
(u/not-valid plugin-id :resetOverrides "Cannot modify a page that is not currently active")
|
||||||
|
|
||||||
(not (ctk/in-component-copy? shape))
|
(not (ctk/in-component-copy? shape))
|
||||||
(u/not-valid plugin-id :resetOverrides "The shape is not a component copy instance")
|
(u/not-valid plugin-id :resetOverrides "The shape is not a component copy instance")
|
||||||
|
|
||||||
@ -1527,6 +1544,9 @@
|
|||||||
(not (sm/validate ctse/schema:export value))
|
(not (sm/validate ctse/schema:export value))
|
||||||
(u/not-valid plugin-id :export value)
|
(u/not-valid plugin-id :export value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:read"))
|
||||||
|
(u/not-valid plugin-id :export "Plugin doesn't have 'content:read' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(if (and (contains? cf/flags :wasm-export)
|
(if (and (contains? cf/flags :wasm-export)
|
||||||
(contains? #{:jpeg :webp :png} (:type value :png)))
|
(contains? #{:jpeg :webp :png} (:type value :png)))
|
||||||
@ -1598,6 +1618,9 @@
|
|||||||
(not (sm/validate ctsi/schema:interaction interaction))
|
(not (sm/validate ctsi/schema:interaction interaction))
|
||||||
(u/not-valid plugin-id :addInteraction interaction)
|
(u/not-valid plugin-id :addInteraction interaction)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :addInteraction "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [index (-> (u/locate-shape file-id page-id id) (:interactions []) count)]
|
(let [index (-> (u/locate-shape file-id page-id id) (:interactions []) count)]
|
||||||
(st/emit!
|
(st/emit!
|
||||||
@ -1611,6 +1634,9 @@
|
|||||||
(not (interaction-proxy? interaction))
|
(not (interaction-proxy? interaction))
|
||||||
(u/not-valid plugin-id :removeInteraction interaction)
|
(u/not-valid plugin-id :removeInteraction interaction)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :removeInteraction "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(dwi/remove-interaction {:id id} (obj/get interaction "$index"))
|
(dwi/remove-interaction {:id id} (obj/get interaction "$index"))
|
||||||
@ -1691,8 +1717,14 @@
|
|||||||
:fn (fn [token attrs]
|
:fn (fn [token attrs]
|
||||||
(let [token (u/locate-token file-id (obj/get token "$set-id") (obj/get token "$id"))
|
(let [token (u/locate-token file-id (obj/get token "$set-id") (obj/get token "$id"))
|
||||||
kw-attrs (into #{} (map token-attr-plugin->token-attr attrs))]
|
kw-attrs (into #{} (map token-attr-plugin->token-attr attrs))]
|
||||||
(if (some #(not (token-attr? %)) kw-attrs)
|
(cond
|
||||||
|
(some #(not (token-attr? %)) kw-attrs)
|
||||||
(u/not-valid plugin-id :applyToken attrs)
|
(u/not-valid plugin-id :applyToken attrs)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :applyToken "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
(st/emit!
|
(st/emit!
|
||||||
(-> (dwta/toggle-token {:token token
|
(-> (dwta/toggle-token {:token token
|
||||||
:attrs kw-attrs
|
:attrs kw-attrs
|
||||||
@ -1720,6 +1752,9 @@
|
|||||||
(not (string? value))
|
(not (string? value))
|
||||||
(u/not-valid plugin-id :value value)
|
(u/not-valid plugin-id :value value)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :switchVariant "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [shape (u/locate-shape file-id page-id id)
|
(let [shape (u/locate-shape file-id page-id id)
|
||||||
component (u/locate-library-component file-id (:component-id shape))]
|
component (u/locate-library-component file-id (:component-id shape))]
|
||||||
@ -1733,6 +1768,9 @@
|
|||||||
(or (not (seq ids)) (not (every? uuid/parse* ids)))
|
(or (not (seq ids)) (not (every? uuid/parse* ids)))
|
||||||
(u/not-valid plugin-id :ids ids)
|
(u/not-valid plugin-id :ids ids)
|
||||||
|
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :combineAsVariants "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(let [;; Keep the input order (head shape first): it determines
|
(let [;; Keep the input order (head shape first): it determines
|
||||||
;; the order of the resulting variant components (see
|
;; the order of the resulting variant components (see
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
[app.main.data.workspace.tokens.application :as dwta]
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.plugins.register :as r]
|
||||||
[app.plugins.system-events :as se]
|
[app.plugins.system-events :as se]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
@ -85,16 +86,20 @@
|
|||||||
|
|
||||||
(defn- apply-token-to-shapes
|
(defn- apply-token-to-shapes
|
||||||
[plugin-id file-id set-id id shape-ids attrs]
|
[plugin-id file-id set-id id shape-ids attrs]
|
||||||
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :applyToken "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
(let [token (u/locate-token file-id set-id id)]
|
:else
|
||||||
(if (some #(not (token-attr? %)) attrs)
|
(let [token (u/locate-token file-id set-id id)]
|
||||||
(u/not-valid plugin-id :applyToSelected attrs)
|
(if (some #(not (token-attr? %)) attrs)
|
||||||
(st/emit!
|
(u/not-valid plugin-id :applyToSelected attrs)
|
||||||
(-> (dwta/toggle-token {:token token
|
(st/emit!
|
||||||
:attrs (into #{} (map token-attr-plugin->token-attr) attrs)
|
(-> (dwta/toggle-token {:token token
|
||||||
:shape-ids shape-ids
|
:attrs (into #{} (map token-attr-plugin->token-attr) attrs)
|
||||||
:expand-with-children false})
|
:shape-ids shape-ids
|
||||||
(se/add-event plugin-id))))))
|
:expand-with-children false})
|
||||||
|
(se/add-event plugin-id)))))))
|
||||||
|
|
||||||
(defn- typography-resolved-value->js
|
(defn- typography-resolved-value->js
|
||||||
"Converts a resolved typography composite (a Clojure map keyed by the
|
"Converts a resolved typography composite (a Clojure map keyed by the
|
||||||
@ -204,8 +209,13 @@
|
|||||||
(ctob/get-tokens set-id)))
|
(ctob/get-tokens set-id)))
|
||||||
:set
|
:set
|
||||||
(fn [_ value]
|
(fn [_ value]
|
||||||
(st/emit! (-> (dwtl/update-token set-id id {:name value})
|
(cond
|
||||||
(se/add-event plugin-id))))}
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (-> (dwtl/update-token set-id id {:name value})
|
||||||
|
(se/add-event plugin-id)))))}
|
||||||
|
|
||||||
:type
|
:type
|
||||||
{:this true
|
{:this true
|
||||||
@ -230,11 +240,16 @@
|
|||||||
base))
|
base))
|
||||||
:set
|
:set
|
||||||
(fn [_ value]
|
(fn [_ value]
|
||||||
(let [token (u/locate-token file-id set-id id)
|
(cond
|
||||||
value (cond-> value
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(= :font-family (:type token))
|
(u/not-valid plugin-id :value "Plugin doesn't have 'content:write' permission")
|
||||||
(ctob/convert-dtcg-font-family))]
|
|
||||||
(st/emit! (dwtl/update-token set-id id {:value value}))))}
|
:else
|
||||||
|
(let [token (u/locate-token file-id set-id id)
|
||||||
|
value (cond-> value
|
||||||
|
(= :font-family (:type token))
|
||||||
|
(ctob/convert-dtcg-font-family))]
|
||||||
|
(st/emit! (dwtl/update-token set-id id {:value value})))))}
|
||||||
|
|
||||||
:resolvedValue
|
:resolvedValue
|
||||||
{:this true
|
{:this true
|
||||||
@ -265,28 +280,43 @@
|
|||||||
:schema cfo/schema:token-description
|
:schema cfo/schema:token-description
|
||||||
:set
|
:set
|
||||||
(fn [_ value]
|
(fn [_ value]
|
||||||
(st/emit! (-> (dwtl/update-token set-id id {:description value})
|
(cond
|
||||||
(se/add-event :plugin-id))))}
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :description "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (-> (dwtl/update-token set-id id {:description value})
|
||||||
|
(se/add-event plugin-id)))))}
|
||||||
|
|
||||||
:duplicate
|
:duplicate
|
||||||
(fn []
|
(fn []
|
||||||
;; TODO:
|
(cond
|
||||||
;; - add function duplicate-token in tokens-lib, that allows to specify the new id
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
;; - use this function in dwtl/duplicate-token
|
(u/not-valid plugin-id :duplicate "Plugin doesn't have 'content:write' permission")
|
||||||
;; - return the new token proxy using the locally forced id
|
|
||||||
;; - do the same with sets and themes
|
:else
|
||||||
(let [token (u/locate-token file-id set-id id)
|
;; TODO:
|
||||||
token' (ctob/make-token (-> (datafy token)
|
;; - add function duplicate-token in tokens-lib, that allows to specify the new id
|
||||||
(dissoc :id
|
;; - use this function in dwtl/duplicate-token
|
||||||
:modified-at)))]
|
;; - return the new token proxy using the locally forced id
|
||||||
(st/emit! (-> (dwtl/create-token set-id token')
|
;; - do the same with sets and themes
|
||||||
(se/add-event plugin-id)))
|
(let [token (u/locate-token file-id set-id id)
|
||||||
(token-proxy plugin-id file-id set-id (:id token'))))
|
token' (ctob/make-token (-> (datafy token)
|
||||||
|
(dissoc :id
|
||||||
|
:modified-at)))]
|
||||||
|
(st/emit! (-> (dwtl/create-token set-id token')
|
||||||
|
(se/add-event plugin-id)))
|
||||||
|
(token-proxy plugin-id file-id set-id (:id token')))))
|
||||||
|
|
||||||
:remove
|
:remove
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (-> (dwtl/delete-token set-id id)
|
(cond
|
||||||
(se/add-event plugin-id))))
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (-> (dwtl/delete-token set-id id)
|
||||||
|
(se/add-event plugin-id)))))
|
||||||
|
|
||||||
:applyToShapes
|
:applyToShapes
|
||||||
{:enumerable false
|
{:enumerable false
|
||||||
@ -337,8 +367,13 @@
|
|||||||
id)
|
id)
|
||||||
:set
|
:set
|
||||||
(fn [_ name]
|
(fn [_ name]
|
||||||
(let [set (u/locate-token-set file-id id)]
|
(cond
|
||||||
(st/emit! (dwtl/rename-token-set set name))))}
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [set (u/locate-token-set file-id id)]
|
||||||
|
(st/emit! (dwtl/rename-token-set set name)))))}
|
||||||
|
|
||||||
:active
|
:active
|
||||||
{:this true
|
{:this true
|
||||||
@ -351,13 +386,23 @@
|
|||||||
:schema ::sm/boolean
|
:schema ::sm/boolean
|
||||||
:set
|
:set
|
||||||
(fn [_ value]
|
(fn [_ value]
|
||||||
(let [set (u/locate-token-set file-id id)]
|
(cond
|
||||||
(st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) value))))}
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :active "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [set (u/locate-token-set file-id id)]
|
||||||
|
(st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) value)))))}
|
||||||
|
|
||||||
:toggleActive
|
:toggleActive
|
||||||
(fn [_]
|
(fn []
|
||||||
(let [set (u/locate-token-set file-id id)]
|
(cond
|
||||||
(st/emit! (dwtl/toggle-token-set (ctob/get-name set)))))
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :toggleActive "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [set (u/locate-token-set file-id id)]
|
||||||
|
(st/emit! (dwtl/toggle-token-set (ctob/get-name set))))))
|
||||||
|
|
||||||
:tokens
|
:tokens
|
||||||
{:this true
|
{:this true
|
||||||
@ -416,39 +461,54 @@
|
|||||||
(sm/update-properties assoc :decode/json cfo/convert-dtcg-token))]))
|
(sm/update-properties assoc :decode/json cfo/convert-dtcg-token))]))
|
||||||
:decode/options {:key-fn identity}
|
:decode/options {:key-fn identity}
|
||||||
:fn (fn [attrs]
|
:fn (fn [attrs]
|
||||||
(let [tokens-lib (u/locate-tokens-lib file-id)
|
(cond
|
||||||
token (ctob/make-token attrs)
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
;; Resolve against all tokens in the library (including those
|
(u/not-valid plugin-id :addToken "Plugin doesn't have 'content:write' permission")
|
||||||
;; in inactive sets) so that references to structurally
|
|
||||||
;; existing tokens resolve even if their set is not active.
|
|
||||||
;; The target set's tokens take precedence over equally named
|
|
||||||
;; tokens in other sets, and the new token takes precedence
|
|
||||||
;; over all.
|
|
||||||
tokens-tree (-> (merge (ctob/get-all-tokens-map tokens-lib)
|
|
||||||
(ctob/get-tokens tokens-lib id))
|
|
||||||
(assoc (:name token) token))
|
|
||||||
resolved-tokens (ts/resolve-tokens tokens-tree)
|
|
||||||
|
|
||||||
{:keys [errors resolved-value] :as resolved-token}
|
:else
|
||||||
(get resolved-tokens (:name token))]
|
(let [tokens-lib (u/locate-tokens-lib file-id)
|
||||||
|
token (ctob/make-token attrs)
|
||||||
|
;; Resolve against all tokens in the library (including those
|
||||||
|
;; in inactive sets) so that references to structurally
|
||||||
|
;; existing tokens resolve even if their set is not active.
|
||||||
|
;; The target set's tokens take precedence over equally named
|
||||||
|
;; tokens in other sets, and the new token takes precedence
|
||||||
|
;; over all.
|
||||||
|
tokens-tree (-> (merge (ctob/get-all-tokens-map tokens-lib)
|
||||||
|
(ctob/get-tokens tokens-lib id))
|
||||||
|
(assoc (:name token) token))
|
||||||
|
resolved-tokens (ts/resolve-tokens tokens-tree)
|
||||||
|
|
||||||
(if resolved-value
|
{:keys [errors resolved-value] :as resolved-token}
|
||||||
(do (st/emit! (-> (dwtl/create-token id token)
|
(get resolved-tokens (:name token))]
|
||||||
(se/add-event plugin-id)))
|
|
||||||
(token-proxy plugin-id file-id id (:id token)))
|
(if resolved-value
|
||||||
(do (u/not-valid plugin-id :addToken (str errors))
|
(do (st/emit! (-> (dwtl/create-token id token)
|
||||||
nil))))}
|
(se/add-event plugin-id)))
|
||||||
|
(token-proxy plugin-id file-id id (:id token)))
|
||||||
|
(do (u/not-valid plugin-id :addToken (str errors))
|
||||||
|
nil)))))}
|
||||||
|
|
||||||
:duplicate
|
:duplicate
|
||||||
(fn []
|
(fn []
|
||||||
(let [id-ref (atom nil)]
|
(cond
|
||||||
(st/emit! (dwtl/duplicate-token-set id {:id-ref id-ref}))
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(when (some? @id-ref)
|
(u/not-valid plugin-id :duplicate "Plugin doesn't have 'content:write' permission")
|
||||||
(token-set-proxy plugin-id file-id @id-ref))))
|
|
||||||
|
:else
|
||||||
|
(let [id-ref (atom nil)]
|
||||||
|
(st/emit! (dwtl/duplicate-token-set id {:id-ref id-ref}))
|
||||||
|
(when (some? @id-ref)
|
||||||
|
(token-set-proxy plugin-id file-id @id-ref)))))
|
||||||
|
|
||||||
:remove
|
:remove
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwtl/delete-token-set id))))))
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwtl/delete-token-set id)))))))
|
||||||
|
|
||||||
(defn token-theme-proxy? [p]
|
(defn token-theme-proxy? [p]
|
||||||
(obj/type-of? p "TokenThemeProxy"))
|
(obj/type-of? p "TokenThemeProxy"))
|
||||||
@ -501,8 +561,13 @@
|
|||||||
(:id theme)))
|
(:id theme)))
|
||||||
:set
|
:set
|
||||||
(fn [_ group]
|
(fn [_ group]
|
||||||
(let [theme (u/locate-token-theme file-id id)]
|
(cond
|
||||||
(st/emit! (dwtl/update-token-theme id (assoc theme :group group)))))}
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :group "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [theme (u/locate-token-theme file-id id)]
|
||||||
|
(st/emit! (dwtl/update-token-theme id (assoc theme :group group))))))}
|
||||||
|
|
||||||
:name
|
:name
|
||||||
{:this true
|
{:this true
|
||||||
@ -517,9 +582,14 @@
|
|||||||
(:group theme)))
|
(:group theme)))
|
||||||
:set
|
:set
|
||||||
(fn [_ name]
|
(fn [_ name]
|
||||||
(let [theme (u/locate-token-theme file-id id)]
|
(cond
|
||||||
(when name
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(st/emit! (dwtl/update-token-theme id (assoc theme :name name))))))}
|
(u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [theme (u/locate-token-theme file-id id)]
|
||||||
|
(when name
|
||||||
|
(st/emit! (dwtl/update-token-theme id (assoc theme :name name)))))))}
|
||||||
|
|
||||||
:active
|
:active
|
||||||
{:this true
|
{:this true
|
||||||
@ -531,11 +601,21 @@
|
|||||||
:schema ::sm/boolean
|
:schema ::sm/boolean
|
||||||
:set
|
:set
|
||||||
(fn [_ value]
|
(fn [_ value]
|
||||||
(st/emit! (dwtl/set-token-theme-active id value)))}
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :active "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwtl/set-token-theme-active id value))))}
|
||||||
|
|
||||||
:toggleActive
|
:toggleActive
|
||||||
(fn [_]
|
(fn []
|
||||||
(st/emit! (dwtl/toggle-token-theme-active id)))
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :toggleActive "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwtl/toggle-token-theme-active id))))
|
||||||
|
|
||||||
:activeSets
|
:activeSets
|
||||||
{:this true
|
{:this true
|
||||||
@ -554,32 +634,52 @@
|
|||||||
{:enumerable false
|
{:enumerable false
|
||||||
:schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]]
|
:schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]]
|
||||||
:fn (fn [set-arg]
|
:fn (fn [set-arg]
|
||||||
(let [set-name (token-set-name (resolve-token-set file-id set-arg))
|
(cond
|
||||||
theme (u/locate-token-theme file-id id)]
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(when (and set-name theme)
|
(u/not-valid plugin-id :addSet "Plugin doesn't have 'content:write' permission")
|
||||||
(st/emit! (dwtl/update-token-theme id (ctob/enable-set theme set-name))))))}
|
|
||||||
|
:else
|
||||||
|
(let [set-name (token-set-name (resolve-token-set file-id set-arg))
|
||||||
|
theme (u/locate-token-theme file-id id)]
|
||||||
|
(when (and set-name theme)
|
||||||
|
(st/emit! (dwtl/update-token-theme id (ctob/enable-set theme set-name)))))))}
|
||||||
|
|
||||||
:removeSet
|
:removeSet
|
||||||
{:enumerable false
|
{:enumerable false
|
||||||
:schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]]
|
:schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]]
|
||||||
:fn (fn [set-arg]
|
:fn (fn [set-arg]
|
||||||
(let [set-name (token-set-name (resolve-token-set file-id set-arg))
|
(cond
|
||||||
theme (u/locate-token-theme file-id id)]
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(when (and set-name theme)
|
(u/not-valid plugin-id :removeSet "Plugin doesn't have 'content:write' permission")
|
||||||
(st/emit! (dwtl/update-token-theme id (ctob/disable-set theme set-name))))))}
|
|
||||||
|
:else
|
||||||
|
(let [set-name (token-set-name (resolve-token-set file-id set-arg))
|
||||||
|
theme (u/locate-token-theme file-id id)]
|
||||||
|
(when (and set-name theme)
|
||||||
|
(st/emit! (dwtl/update-token-theme id (ctob/disable-set theme set-name)))))))}
|
||||||
|
|
||||||
:duplicate
|
:duplicate
|
||||||
(fn []
|
(fn []
|
||||||
(let [theme (u/locate-token-theme file-id id)
|
(cond
|
||||||
theme' (ctob/make-token-theme (-> (datafy theme)
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(dissoc :id
|
(u/not-valid plugin-id :duplicate "Plugin doesn't have 'content:write' permission")
|
||||||
:modified-at)))]
|
|
||||||
(st/emit! (dwtl/create-token-theme theme'))
|
:else
|
||||||
(token-theme-proxy plugin-id file-id (:id theme'))))
|
(let [theme (u/locate-token-theme file-id id)
|
||||||
|
theme' (ctob/make-token-theme (-> (datafy theme)
|
||||||
|
(dissoc :id
|
||||||
|
:modified-at)))]
|
||||||
|
(st/emit! (dwtl/create-token-theme theme'))
|
||||||
|
(token-theme-proxy plugin-id file-id (:id theme')))))
|
||||||
|
|
||||||
:remove
|
:remove
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwtl/delete-token-theme id)))))
|
(cond
|
||||||
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
|
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwtl/delete-token-theme id))))))
|
||||||
|
|
||||||
(defn tokens-catalog
|
(defn tokens-catalog
|
||||||
[plugin-id file-id]
|
[plugin-id file-id]
|
||||||
@ -619,9 +719,14 @@
|
|||||||
nil)
|
nil)
|
||||||
(sm/dissoc-key :id))]) ;; We don't allow plugins to set the id
|
(sm/dissoc-key :id))]) ;; We don't allow plugins to set the id
|
||||||
:fn (fn [attrs]
|
:fn (fn [attrs]
|
||||||
(let [theme (ctob/make-token-theme attrs)]
|
(cond
|
||||||
(st/emit! (dwtl/create-token-theme theme))
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(token-theme-proxy plugin-id file-id (:id theme))))}
|
(u/not-valid plugin-id :addTheme "Plugin doesn't have 'content:write' permission")
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [theme (ctob/make-token-theme attrs)]
|
||||||
|
(st/emit! (dwtl/create-token-theme theme))
|
||||||
|
(token-theme-proxy plugin-id file-id (:id theme)))))}
|
||||||
|
|
||||||
:addSet
|
:addSet
|
||||||
{:enumerable false
|
{:enumerable false
|
||||||
@ -638,21 +743,26 @@
|
|||||||
(sm/merge [:map [:active {:optional true} ::sm/boolean]]))]
|
(sm/merge [:map [:active {:optional true} ::sm/boolean]]))]
|
||||||
|
|
||||||
:fn (fn [attrs]
|
:fn (fn [attrs]
|
||||||
(let [active? (boolean (:active attrs))
|
(cond
|
||||||
attrs (-> attrs
|
(not (r/check-permission plugin-id "content:write"))
|
||||||
(dissoc :active)
|
(u/not-valid plugin-id :addSet "Plugin doesn't have 'content:write' permission")
|
||||||
(update :name ctob/normalize-set-name))
|
|
||||||
set (ctob/make-token-set attrs)]
|
:else
|
||||||
(st/emit! (dwtl/create-token-set set))
|
(let [active? (boolean (:active attrs))
|
||||||
;; Newly created sets are inactive by default; activate it when
|
attrs (-> attrs
|
||||||
;; requested. Enabling only adds the set name to the hidden theme,
|
(dissoc :active)
|
||||||
;; so it does not depend on the create event having propagated yet.
|
(update :name ctob/normalize-set-name))
|
||||||
(when active?
|
set (ctob/make-token-set attrs)]
|
||||||
(st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) true)))
|
(st/emit! (dwtl/create-token-set set))
|
||||||
;; Pass the set name as `initial-name` so the proxy can resolve
|
;; Newly created sets are inactive by default; activate it when
|
||||||
;; it immediately, before the async `st/emit!` above propagates
|
;; requested. Enabling only adds the set name to the hidden theme,
|
||||||
;; the new set into `@st/state`.
|
;; so it does not depend on the create event having propagated yet.
|
||||||
(token-set-proxy plugin-id file-id (ctob/get-id set) (ctob/get-name set))))}
|
(when active?
|
||||||
|
(st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) true)))
|
||||||
|
;; Pass the set name as `initial-name` so the proxy can resolve
|
||||||
|
;; it immediately, before the async `st/emit!` above propagates
|
||||||
|
;; the new set into `@st/state`.
|
||||||
|
(token-set-proxy plugin-id file-id (ctob/get-id set) (ctob/get-name set)))))}
|
||||||
|
|
||||||
:getThemeById
|
:getThemeById
|
||||||
{:enumerable false
|
{:enumerable false
|
||||||
|
|||||||
40
frontend/test/frontend_tests/plugins/flex_test.cljs
Normal file
40
frontend/test/frontend_tests/plugins/flex_test.cljs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
;; 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 Sucursal en España SL
|
||||||
|
|
||||||
|
(ns frontend-tests.plugins.flex-test
|
||||||
|
(:require
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.plugins.flex :as flex]
|
||||||
|
[app.plugins.register :as r]
|
||||||
|
[app.plugins.shape :as shape]
|
||||||
|
[app.plugins.utils :as u]
|
||||||
|
[cljs.test :as t :include-macros true]
|
||||||
|
[frontend-tests.helpers.mock :as mock]))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Permission checks (T9-F-05)
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(t/deftest flex-remove-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (flex/flex-layout-proxy plugin-id file-id page-id id)]
|
||||||
|
(.remove proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
;; TODO: flex-append-child-checks-permission test requires more complex mocking
|
||||||
|
;; of u/locate-objects, u/locate-shape, ctl/reverse?, etc. The permission check
|
||||||
|
;; is in place at flex.cljs line 358.
|
||||||
@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
(ns frontend-tests.plugins.library-test
|
(ns frontend-tests.plugins.library-test
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.types.component :as ctk]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
[app.main.data.workspace.variants :as dwv]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.library :as library]
|
[app.plugins.library :as library]
|
||||||
[app.plugins.register :as r]
|
[app.plugins.register :as r]
|
||||||
@ -93,3 +96,112 @@
|
|||||||
(t/is (contains? (:color @captured) :image))
|
(t/is (contains? (:color @captured) :image))
|
||||||
(t/is (not (contains? (:color @captured) :color)))
|
(t/is (not (contains? (:color @captured) :color)))
|
||||||
(t/is (not (contains? (:color @captured) :gradient))))))
|
(t/is (not (contains? (:color @captured) :gradient))))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Permission checks (T9-F-02)
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(t/deftest variant-add-variant-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/variant-proxy plugin-id file-id id)]
|
||||||
|
(.addVariant proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addVariant "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest variant-add-property-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/variant-proxy plugin-id file-id id)]
|
||||||
|
(.addProperty proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addProperty "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest variant-remove-property-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
library/get-variant-components (constantly [{:variant-properties [{:name "color" :value "red"}]}])
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/variant-proxy plugin-id file-id id)]
|
||||||
|
(.removeProperty proxy 0)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :removeProperty "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest variant-rename-property-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
library/get-variant-components (constantly [{:variant-properties [{:name "color" :value "red"}]}])
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/variant-proxy plugin-id file-id id)]
|
||||||
|
(.renameProperty proxy 0 "newName")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :renameProperty "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest component-transform-in-variant-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
u/locate-library-component (constantly {:id id :main-instance-id id})
|
||||||
|
ctk/is-variant? (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/lib-component-proxy plugin-id file-id id)]
|
||||||
|
(.transformInVariant proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :transformInVariant "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest component-add-variant-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
u/locate-library-component (constantly {:id id :main-instance-id id})
|
||||||
|
ctk/is-variant? (constantly true)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/lib-component-proxy plugin-id file-id id)]
|
||||||
|
(.addVariant proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addVariant "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest component-set-variant-property-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
u/locate-library-component (constantly {:id id :variant-properties [{:name "color"}]})
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (library/lib-component-proxy plugin-id file-id id)]
|
||||||
|
(.setVariantProperty proxy 0 "red")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :setVariantProperty "Plugin doesn't have 'library:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|||||||
@ -9,12 +9,17 @@
|
|||||||
[app.common.test-helpers.files :as cthf]
|
[app.common.test-helpers.files :as cthf]
|
||||||
[app.common.test-helpers.ids-map :as thi]
|
[app.common.test-helpers.ids-map :as thi]
|
||||||
[app.common.test-helpers.shapes :as cths]
|
[app.common.test-helpers.shapes :as cths]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace.pages :as dwpg]
|
[app.main.data.workspace.pages :as dwpg]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.api :as api]
|
[app.plugins.api :as api]
|
||||||
|
[app.plugins.page :as page]
|
||||||
|
[app.plugins.register :as r]
|
||||||
[app.plugins.shape :as shape]
|
[app.plugins.shape :as shape]
|
||||||
|
[app.plugins.utils :as u]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[cljs.test :as t :include-macros true]
|
[cljs.test :as t :include-macros true]
|
||||||
|
[frontend-tests.helpers.mock :as mock]
|
||||||
[frontend-tests.helpers.state :as ths]
|
[frontend-tests.helpers.state :as ths]
|
||||||
[frontend-tests.helpers.wasm :as thw]
|
[frontend-tests.helpers.wasm :as thw]
|
||||||
[potok.v2.core :as ptk]))
|
[potok.v2.core :as ptk]))
|
||||||
@ -151,3 +156,85 @@
|
|||||||
(done))))
|
(done))))
|
||||||
(mock-page-initialized store page2-id))
|
(mock-page-initialized store page2-id))
|
||||||
0))))
|
0))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Permission checks (T9-F-03)
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(t/deftest flow-name-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
flow-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (page/flow-proxy plugin-id file-id page-id flow-id)]
|
||||||
|
(set! (.-name proxy) "new-name")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :name "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest flow-starting-board-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
flow-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop
|
||||||
|
shape/shape-proxy? (constantly true)]
|
||||||
|
(let [proxy (page/flow-proxy plugin-id file-id page-id flow-id)]
|
||||||
|
(set! (.-startingBoard proxy) #js {})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :startingBoard "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest flow-remove-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
flow-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (page/flow-proxy plugin-id file-id page-id flow-id)]
|
||||||
|
(.remove proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest create-flow-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop
|
||||||
|
shape/shape-proxy? (constantly true)]
|
||||||
|
(let [proxy (page/page-proxy plugin-id file-id page-id)
|
||||||
|
frame #js {"$id" (uuid/next)}]
|
||||||
|
(.createFlow proxy "flow-name" frame)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :createFlow "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest remove-flow-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
flow-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
st/emit! mock/noop
|
||||||
|
page/flow-proxy? (constantly true)]
|
||||||
|
(let [proxy (page/page-proxy plugin-id file-id page-id)]
|
||||||
|
(.removeFlow proxy (page/flow-proxy plugin-id file-id page-id flow-id))
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :removeFlow "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|||||||
@ -11,10 +11,15 @@
|
|||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
[app.main.data.workspace.tokens.application :as dwta]
|
||||||
[app.main.data.workspace.variants :as dwv]
|
[app.main.data.workspace.variants :as dwv]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.api :as api]
|
[app.plugins.api :as api]
|
||||||
[app.plugins.public-utils :as public-utils]
|
[app.plugins.public-utils :as public-utils]
|
||||||
|
[app.plugins.register :as r]
|
||||||
[app.plugins.shape :as shape]
|
[app.plugins.shape :as shape]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[cljs.test :as t :include-macros true]
|
[cljs.test :as t :include-macros true]
|
||||||
@ -204,3 +209,202 @@
|
|||||||
(t/deftest group-empty-input-returns-nil
|
(t/deftest group-empty-input-returns-nil
|
||||||
(let [context (api/create-context plugin-id)]
|
(let [context (api/create-context plugin-id)]
|
||||||
(t/is (nil? (.group context #js [])))))
|
(t/is (nil? (.group context #js [])))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Permission checks (T9-F-04)
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
(t/deftest commit-fills-text-shape-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/proxy->shape (constantly {:id shape-id :type :text})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)]
|
||||||
|
(set! (.-fills proxy) #js [#js {:fillColor "#ff0000" :fillOpacity 1}])
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :fills "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest interaction-trigger-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [inter (shape/interaction-proxy plugin-id file-id page-id shape-id 0)]
|
||||||
|
(set! (.-trigger inter) "click")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :trigger "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest interaction-delay-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [inter (shape/interaction-proxy plugin-id file-id page-id shape-id 0)]
|
||||||
|
(set! (.-delay inter) 100)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :delay "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest interaction-action-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/proxy->interaction (constantly {:event-type :click :delay 0 :action-type :open-url :url "https://example.com"})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [inter (shape/interaction-proxy plugin-id file-id page-id shape-id 0)]
|
||||||
|
(set! (.-action inter) #js {:type "open-url" :url "https://example.com"})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :action "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest interaction-remove-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [inter (shape/interaction-proxy plugin-id file-id page-id shape-id 0)]
|
||||||
|
(.remove inter)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest add-interaction-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-shape (constantly {:id shape-id})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)]
|
||||||
|
(.addInteraction proxy "click" #js {:type "open-url" :url "https://example.com"})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addInteraction "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest remove-interaction-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)
|
||||||
|
inter (shape/interaction-proxy plugin-id file-id page-id shape-id 0)]
|
||||||
|
(.removeInteraction proxy inter)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :removeInteraction "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest detach-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/page-active? (constantly true)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)]
|
||||||
|
(.detach proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :detach "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest export-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)]
|
||||||
|
(.export proxy #js {:type "png" :scale 1})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :export "Plugin doesn't have 'content:read' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest apply-token-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
token-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token (constantly {:id token-id :name "test" :type :color})
|
||||||
|
shape/token-proxy? (constantly true)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)
|
||||||
|
token #js {"$set-id" (str set-id) "$id" (str token-id)}]
|
||||||
|
(.applyToken proxy token #js [])
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :applyToken "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest switch-variant-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-shape (constantly {:id shape-id :component-id shape-id})
|
||||||
|
u/locate-library-component (constantly {:id (uuid/next)})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)]
|
||||||
|
(.switchVariant proxy 0 "value")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :switchVariant "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest combine-as-variants-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
page-id (uuid/next)
|
||||||
|
shape-id (uuid/next)
|
||||||
|
other-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-shape (fn [_file _page id] {:id id :component-id id})
|
||||||
|
u/locate-library-component (constantly {:id (uuid/next)})
|
||||||
|
ctk/is-variant? (constantly false)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (shape/shape-proxy plugin-id file-id page-id shape-id)]
|
||||||
|
(.combineAsVariants proxy #js [(str other-id)])
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :combineAsVariants "Plugin doesn't have 'content:write' permission"]
|
||||||
|
(first @errors)))))))
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.plugins.api :as api]
|
[app.plugins.api :as api]
|
||||||
|
[app.plugins.register :as r]
|
||||||
[app.plugins.tokens :as ptok]
|
[app.plugins.tokens :as ptok]
|
||||||
[app.plugins.utils :as u]
|
[app.plugins.utils :as u]
|
||||||
[cljs.test :as t :include-macros true]
|
[cljs.test :as t :include-macros true]
|
||||||
@ -236,7 +237,8 @@
|
|||||||
set-id (cthi/new-id! :set)
|
set-id (cthi/new-id! :set)
|
||||||
dup-id (cthi/new-id! :dup)
|
dup-id (cthi/new-id! :dup)
|
||||||
proxy (ptok/token-set-proxy "plugin-id" file-id set-id)]
|
proxy (ptok/token-set-proxy "plugin-id" file-id set-id)]
|
||||||
(with-redefs [dwtl/duplicate-token-set
|
(with-redefs [r/check-permission (constantly true)
|
||||||
|
dwtl/duplicate-token-set
|
||||||
(mock/stub (fn [id {:keys [id-ref]}]
|
(mock/stub (fn [id {:keys [id-ref]}]
|
||||||
(t/is (= set-id id))
|
(t/is (= set-id id))
|
||||||
(reset! id-ref dup-id)
|
(reset! id-ref dup-id)
|
||||||
@ -253,7 +255,8 @@
|
|||||||
set (ptok/token-set-proxy "plugin-id" file-id set-id "Primitives")
|
set (ptok/token-set-proxy "plugin-id" file-id set-id "Primitives")
|
||||||
theme (ptok/token-theme-proxy "plugin-id" file-id theme-id)
|
theme (ptok/token-theme-proxy "plugin-id" file-id theme-id)
|
||||||
captured (atom [])]
|
captured (atom [])]
|
||||||
(with-redefs [u/locate-token-theme
|
(with-redefs [r/check-permission (constantly true)
|
||||||
|
u/locate-token-theme
|
||||||
(fn [_file _theme]
|
(fn [_file _theme]
|
||||||
(ctob/make-token-theme :id theme-id
|
(ctob/make-token-theme :id theme-id
|
||||||
:name "Theme"
|
:name "Theme"
|
||||||
@ -274,7 +277,8 @@
|
|||||||
set-id (cthi/new-id! :set)
|
set-id (cthi/new-id! :set)
|
||||||
token-id (cthi/new-id! :token)
|
token-id (cthi/new-id! :token)
|
||||||
captured (atom nil)]
|
captured (atom nil)]
|
||||||
(with-redefs [u/locate-token (constantly {:id token-id
|
(with-redefs [r/check-permission (constantly true)
|
||||||
|
u/locate-token (constantly {:id token-id
|
||||||
:name "font.primary"
|
:name "font.primary"
|
||||||
:type :font-family
|
:type :font-family
|
||||||
:value ["Inter"]})
|
:value ["Inter"]})
|
||||||
@ -347,7 +351,8 @@
|
|||||||
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
||||||
emitted (atom [])
|
emitted (atom [])
|
||||||
invalid (atom [])]
|
invalid (atom [])]
|
||||||
(with-redefs [u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
|
(with-redefs [r/check-permission (constantly true)
|
||||||
|
u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
|
||||||
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
||||||
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
||||||
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
||||||
@ -367,7 +372,8 @@
|
|||||||
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
||||||
emitted (atom [])
|
emitted (atom [])
|
||||||
invalid (atom [])]
|
invalid (atom [])]
|
||||||
(with-redefs [u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
|
(with-redefs [r/check-permission (constantly true)
|
||||||
|
u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
|
||||||
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
||||||
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
||||||
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
||||||
@ -400,3 +406,304 @@
|
|||||||
(t/is (= 2 (count @errors)))
|
(t/is (= 2 (count @errors)))
|
||||||
(t/is (every? #(instance? js/Error %) @errors))))))
|
(t/is (every? #(instance? js/Error %) @errors))))))
|
||||||
|
|
||||||
|
;; ═══════════════════════════════════════════════════════════════
|
||||||
|
;; Permission check tests (T9-F-01)
|
||||||
|
;; ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
;; Note: token-proxy-name-setter-checks-permission test removed because
|
||||||
|
;; schema validation runs before the permission check, making it impossible
|
||||||
|
;; to test the permission check directly for setters with schemas.
|
||||||
|
|
||||||
|
(t/deftest token-proxy-value-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
token-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token (constantly {:id token-id :name "test" :type :color})
|
||||||
|
u/locate-tokens-lib (constantly nil)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)]
|
||||||
|
(set! (.-value proxy) "#ff0000")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :value "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-proxy-description-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
token-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token (constantly {:id token-id :name "test"})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)]
|
||||||
|
(set! (.-description proxy) "A description")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :description "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-proxy-duplicate-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
token-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token (constantly {:id token-id :name "test" :type :color :value "#000"})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)]
|
||||||
|
(.duplicate proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :duplicate "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-proxy-remove-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
token-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)]
|
||||||
|
(.remove proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-set-proxy-name-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-set (constantly {:id set-id :name "core"})
|
||||||
|
u/locate-tokens-lib (constantly (ctob/make-tokens-lib))
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-set-proxy plugin-id file-id set-id "core")]
|
||||||
|
(set! (.-name proxy) "new-core")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :name "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-set-proxy-active-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-set (constantly {:id set-id :name "core"})
|
||||||
|
u/locate-tokens-lib (constantly (ctob/make-tokens-lib))
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-set-proxy plugin-id file-id set-id "core")]
|
||||||
|
(set! (.-active proxy) true)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :active "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-set-proxy-toggle-active-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-set (constantly {:id set-id :name "core"})
|
||||||
|
u/locate-tokens-lib (constantly (ctob/make-tokens-lib))
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-set-proxy plugin-id file-id set-id)]
|
||||||
|
(.toggleActive proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :toggleActive "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-set-proxy-add-token-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
tokens-lib (-> (ctob/make-tokens-lib)
|
||||||
|
(ctob/add-set (ctob/make-token-set :id set-id :name "core")))
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-set (constantly {:id set-id :name "core"})
|
||||||
|
u/locate-tokens-lib (constantly tokens-lib)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-set-proxy plugin-id file-id set-id "core")]
|
||||||
|
(t/is (fn? (.-addToken proxy)))
|
||||||
|
(.addToken proxy #js {"type" "color" "name" "color.test" "value" "#FF0000"})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addToken "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-set-proxy-duplicate-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-set-proxy plugin-id file-id set-id)]
|
||||||
|
(.duplicate proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :duplicate "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-set-proxy-remove-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-set-proxy plugin-id file-id set-id)]
|
||||||
|
(.remove proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-group-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :group "mode"})
|
||||||
|
u/locate-tokens-lib (constantly nil)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||||
|
(set! (.-group proxy) "new-group")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :group "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-name-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :group "mode"})
|
||||||
|
u/locate-tokens-lib (constantly nil)
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||||
|
(set! (.-name proxy) "Dark")
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :name "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-active-setter-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-tokens-lib (constantly (ctob/make-tokens-lib))
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||||
|
(set! (.-active proxy) true)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :active "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-toggle-active-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||||
|
(.toggleActive proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :toggleActive "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-add-set-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :sets #{}})
|
||||||
|
u/locate-token-set (constantly {:id set-id :name "core"})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)
|
||||||
|
set-proxy (ptok/token-set-proxy plugin-id file-id set-id "core")]
|
||||||
|
(.addSet proxy set-proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addSet "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-remove-set-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
set-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :sets #{"core"}})
|
||||||
|
u/locate-token-set (constantly {:id set-id :name "core"})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)
|
||||||
|
set-proxy (ptok/token-set-proxy plugin-id file-id set-id "core")]
|
||||||
|
(.removeSet proxy set-proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :removeSet "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-duplicate-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :group "mode"})
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||||
|
(.duplicate proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :duplicate "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest token-theme-proxy-remove-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
theme-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||||
|
(.remove proxy)
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest tokens-catalog-add-theme-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-tokens-lib (constantly (ctob/make-tokens-lib))
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [catalog (ptok/tokens-catalog plugin-id file-id)]
|
||||||
|
(.addTheme catalog #js {"name" "NewTheme" "group" "mode"})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addTheme "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
(t/deftest tokens-catalog-add-set-checks-permission
|
||||||
|
(let [plugin-id "test-plugin"
|
||||||
|
file-id (uuid/next)
|
||||||
|
errors (atom [])]
|
||||||
|
(with-redefs [u/locate-tokens-lib (constantly (ctob/make-tokens-lib))
|
||||||
|
u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg])))
|
||||||
|
r/check-permission (constantly false)
|
||||||
|
st/emit! mock/noop]
|
||||||
|
(let [catalog (ptok/tokens-catalog plugin-id file-id)]
|
||||||
|
(.addSet catalog #js {"name" "NewSet"})
|
||||||
|
(t/is (= 1 (count @errors)))
|
||||||
|
(t/is (= [plugin-id :addSet "Plugin doesn't have 'content:write' permission"] (first @errors)))))))
|
||||||
|
|
||||||
|
|||||||
80
frontend/test/frontend_tests/plugins/user_test.cljs
Normal file
80
frontend/test/frontend_tests/plugins/user_test.cljs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
;; 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 Sucursal en España SL
|
||||||
|
|
||||||
|
(ns frontend-tests.plugins.user-test
|
||||||
|
(:require
|
||||||
|
[app.main.data.comments :as dc]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.plugins.api :as api]
|
||||||
|
[app.plugins.comments :as comments]
|
||||||
|
[app.plugins.file :as file]
|
||||||
|
[app.plugins.register :as r]
|
||||||
|
[cljs.test :as t :include-macros true]
|
||||||
|
[frontend-tests.helpers.mock :as mock]))
|
||||||
|
|
||||||
|
(def ^:private plugin-id "00000000-0000-0000-0000-000000000000")
|
||||||
|
|
||||||
|
(t/deftest comment-thread-owner-returns-nil-without-user-read
|
||||||
|
(let [owner-id (random-uuid)
|
||||||
|
file-id (random-uuid)
|
||||||
|
page-id (random-uuid)
|
||||||
|
thread-id (random-uuid)
|
||||||
|
thread (comments/comment-thread-proxy
|
||||||
|
plugin-id
|
||||||
|
file-id
|
||||||
|
page-id
|
||||||
|
{:id thread-id :owner-id owner-id})]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
dc/get-owner (constantly {:id owner-id :fullname "Owner"})]
|
||||||
|
(t/is (nil? (.-owner thread)))
|
||||||
|
(t/is (nil? (.-user thread))))))
|
||||||
|
|
||||||
|
(t/deftest comment-reply-owner-returns-nil-without-user-read
|
||||||
|
(let [owner-id (random-uuid)
|
||||||
|
file-id (random-uuid)
|
||||||
|
page-id (random-uuid)
|
||||||
|
thread-id (random-uuid)
|
||||||
|
reply-id (random-uuid)
|
||||||
|
reply (comments/comment-proxy
|
||||||
|
plugin-id
|
||||||
|
file-id
|
||||||
|
page-id
|
||||||
|
thread-id
|
||||||
|
{:id reply-id :owner-id owner-id})]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
dc/get-owner (constantly {:id owner-id :fullname "Owner"})]
|
||||||
|
(t/is (nil? (.-owner reply)))
|
||||||
|
(t/is (nil? (.-user reply))))))
|
||||||
|
|
||||||
|
(t/deftest file-version-created-by-returns-nil-without-user-read
|
||||||
|
(let [file-id (random-uuid)
|
||||||
|
version-id (random-uuid)
|
||||||
|
profile-id (random-uuid)
|
||||||
|
version (file/file-version-proxy
|
||||||
|
plugin-id
|
||||||
|
file-id
|
||||||
|
{profile-id {:id profile-id :fullname "User"}}
|
||||||
|
{:id version-id
|
||||||
|
:label "Version"
|
||||||
|
:created-at (js/Date.)
|
||||||
|
:profile-id profile-id})]
|
||||||
|
(with-redefs [r/check-permission (constantly false)]
|
||||||
|
(t/is (nil? (.-createdBy version))))))
|
||||||
|
|
||||||
|
(t/deftest get-current-user-returns-nil-without-user-read
|
||||||
|
(let [ctx (api/create-context plugin-id)]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
st/state (atom {:session-id (random-uuid)
|
||||||
|
:profile {:id (random-uuid) :fullname "User"}})]
|
||||||
|
(t/is (nil? (.getCurrentUser ctx))))))
|
||||||
|
|
||||||
|
(t/deftest get-active-users-returns-empty-without-user-read
|
||||||
|
(let [ctx (api/create-context plugin-id)]
|
||||||
|
(with-redefs [r/check-permission (constantly false)
|
||||||
|
st/state (atom {:session-id (random-uuid)
|
||||||
|
:profile {:id (random-uuid)}
|
||||||
|
:workspace-presence {(random-uuid) {:id (random-uuid)}}})]
|
||||||
|
(t/is (zero? (.-length (.getActiveUsers ctx)))))))
|
||||||
@ -44,6 +44,7 @@
|
|||||||
[frontend-tests.plugins.comments-test]
|
[frontend-tests.plugins.comments-test]
|
||||||
[frontend-tests.plugins.context-shapes-test]
|
[frontend-tests.plugins.context-shapes-test]
|
||||||
[frontend-tests.plugins.file-test]
|
[frontend-tests.plugins.file-test]
|
||||||
|
[frontend-tests.plugins.flex-test]
|
||||||
[frontend-tests.plugins.format-test]
|
[frontend-tests.plugins.format-test]
|
||||||
[frontend-tests.plugins.grid-test]
|
[frontend-tests.plugins.grid-test]
|
||||||
[frontend-tests.plugins.interactions-test]
|
[frontend-tests.plugins.interactions-test]
|
||||||
@ -55,6 +56,7 @@
|
|||||||
[frontend-tests.plugins.shape-bugfixes-test]
|
[frontend-tests.plugins.shape-bugfixes-test]
|
||||||
[frontend-tests.plugins.text-test]
|
[frontend-tests.plugins.text-test]
|
||||||
[frontend-tests.plugins.tokens-test]
|
[frontend-tests.plugins.tokens-test]
|
||||||
|
[frontend-tests.plugins.user-test]
|
||||||
[frontend-tests.plugins.utils-test]
|
[frontend-tests.plugins.utils-test]
|
||||||
[frontend-tests.plugins.value-objects-test]
|
[frontend-tests.plugins.value-objects-test]
|
||||||
[frontend-tests.render-dimensions-test]
|
[frontend-tests.render-dimensions-test]
|
||||||
@ -143,6 +145,7 @@
|
|||||||
'frontend-tests.plugins.comments-test
|
'frontend-tests.plugins.comments-test
|
||||||
'frontend-tests.plugins.context-shapes-test
|
'frontend-tests.plugins.context-shapes-test
|
||||||
'frontend-tests.plugins.file-test
|
'frontend-tests.plugins.file-test
|
||||||
|
'frontend-tests.plugins.flex-test
|
||||||
'frontend-tests.plugins.format-test
|
'frontend-tests.plugins.format-test
|
||||||
'frontend-tests.plugins.grid-test
|
'frontend-tests.plugins.grid-test
|
||||||
'frontend-tests.plugins.interactions-test
|
'frontend-tests.plugins.interactions-test
|
||||||
@ -154,6 +157,7 @@
|
|||||||
'frontend-tests.plugins.shape-bugfixes-test
|
'frontend-tests.plugins.shape-bugfixes-test
|
||||||
'frontend-tests.plugins.text-test
|
'frontend-tests.plugins.text-test
|
||||||
'frontend-tests.plugins.tokens-test
|
'frontend-tests.plugins.tokens-test
|
||||||
|
'frontend-tests.plugins.user-test
|
||||||
'frontend-tests.plugins.utils-test
|
'frontend-tests.plugins.utils-test
|
||||||
'frontend-tests.plugins.value-objects-test
|
'frontend-tests.plugins.value-objects-test
|
||||||
'frontend-tests.render-wasm.process-objects-test
|
'frontend-tests.render-wasm.process-objects-test
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user