From 73e35555ab0235f26b939faeba556de7766a1dc6 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 18 Aug 2026 11:34:06 +0000 Subject: [PATCH] :bug: 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 --- frontend/src/app/plugins/api.cljs | 16 ++-- frontend/src/app/plugins/comments.cljs | 15 ++-- frontend/src/app/plugins/file.cljs | 5 +- .../frontend_tests/plugins/user_test.cljs | 80 +++++++++++++++++++ frontend/test/frontend_tests/runner.cljs | 2 + 5 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 frontend/test/frontend_tests/plugins/user_test.cljs diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index bea36ad027..85ad73d70f 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -47,6 +47,7 @@ [app.plugins.local-storage :as local-storage] [app.plugins.page :as page] [app.plugins.parser :as parser] + [app.plugins.register :as r] [app.plugins.shape :as shape] [app.plugins.system-events :as se] [app.plugins.user :as user] @@ -242,15 +243,18 @@ :getCurrentUser (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 (fn [] - (apply array - (->> (:workspace-presence @st/state) - (vals) - (remove #(= (:id %) (:session-id @st/state))) - (map #(user/active-user-proxy plugin-id (:id %)))))) + (if (r/check-permission plugin-id "user:read") + (apply array + (->> (:workspace-presence @st/state) + (vals) + (remove #(= (:id %) (:session-id @st/state))) + (map #(user/active-user-proxy plugin-id (:id %))))) + (array))) :uploadMediaUrl (fn [name url] diff --git a/frontend/src/app/plugins/comments.cljs b/frontend/src/app/plugins/comments.cljs index 71e8a0311b..b45b36b552 100644 --- a/frontend/src/app/plugins/comments.cljs +++ b/frontend/src/app/plugins/comments.cljs @@ -40,12 +40,14 @@ ;; FIXME: inconsistent with comment-thread: owner :user - {:get #(->> (dc/get-owner data) - (user/user-proxy plugin-id))} + {:get #(when (r/check-permission plugin-id "user:read") + (->> (dc/get-owner data) + (user/user-proxy plugin-id)))} :owner - {:get #(->> (dc/get-owner data) - (user/user-proxy plugin-id))} + {:get #(when (r/check-permission plugin-id "user:read") + (->> (dc/get-owner data) + (user/user-proxy plugin-id)))} :date {:get @@ -116,8 +118,9 @@ :board {:get #(shape/shape-proxy plugin-id file-id page-id (:frame-id data))} :owner - {:get #(->> (dc/get-owner data) - (user/user-proxy plugin-id))} + {:get #(when (r/check-permission plugin-id "user:read") + (->> (dc/get-owner data) + (user/user-proxy plugin-id)))} :position {:get diff --git a/frontend/src/app/plugins/file.cljs b/frontend/src/app/plugins/file.cljs index 12049611bd..10d6895d65 100644 --- a/frontend/src/app/plugins/file.cljs +++ b/frontend/src/app/plugins/file.cljs @@ -61,8 +61,9 @@ :createdBy {:get (fn [] - (when-let [user-data (get users (:profile-id @data))] - (user/user-proxy plugin-id user-data)))} + (when (r/check-permission plugin-id "user:read") + (when-let [user-data (get users (:profile-id @data))] + (user/user-proxy plugin-id user-data))))} :createdAt {:get #(:created-at @data)} diff --git a/frontend/test/frontend_tests/plugins/user_test.cljs b/frontend/test/frontend_tests/plugins/user_test.cljs new file mode 100644 index 0000000000..99c83081bd --- /dev/null +++ b/frontend/test/frontend_tests/plugins/user_test.cljs @@ -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))))))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index eedb9b78d1..495a679615 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -53,6 +53,7 @@ [frontend-tests.plugins.shape-bugfixes-test] [frontend-tests.plugins.text-test] [frontend-tests.plugins.tokens-test] + [frontend-tests.plugins.user-test] [frontend-tests.plugins.utils-test] [frontend-tests.plugins.value-objects-test] [frontend-tests.render-dimensions-test] @@ -150,6 +151,7 @@ 'frontend-tests.plugins.shape-bugfixes-test 'frontend-tests.plugins.text-test 'frontend-tests.plugins.tokens-test + 'frontend-tests.plugins.user-test 'frontend-tests.plugins.utils-test 'frontend-tests.plugins.value-objects-test 'frontend-tests.render-wasm.process-objects-test