From ea265da1f37aa8a8b114594aaee96a1f7d44770a Mon Sep 17 00:00:00 2001 From: boskodev790 Date: Mon, 27 Apr 2026 10:59:09 -0500 Subject: [PATCH] :bug: Fix plugin library.connectLibrary breaking Promise contract on permission failure (#9158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `library.connectLibrary()` declared its permission check **outside** the `js/Promise.` wrapper, so when a plugin without `library:write` permission called `await library.connectLibrary(id)` the method did not return a `Promise` at all: - With the default `throwValidationErrors` flag off → `u/not-valid` logs to console and returns `nil`. `await nil` resolves to `nil`, so the plugin sees a "successful" result and crashes later when it tries to use methods on what it thinks is a `LibraryProxy`. - With `throwValidationErrors` on → `u/not-valid` throws synchronously, so the caller gets a thrown exception instead of a rejected promise — inconsistent with every other `library:*` / `content:*` method which always returns a Promise that rejects via `reject-not-valid`. Additionally, the in-Promise `(not (string? library-id))` branch used `(reject nil)` — the plugin got a rejected Promise but with no error message. Move the permission check inside the Promise constructor and replace both validation errors with `u/reject-not-valid`, matching the pattern used by the sibling methods `restore`, `remove`, `pin`, `saveVersion`, `findVersions` in `frontend/src/app/plugins/file.cljs` and every other promise-returning plugin method. No new imports. Also add a CHANGES.md entry under the 2.17.0 Unreleased bugs-fixed section. Signed-off-by: Andrey Antukh Co-authored-by: Andrey Antukh --- CHANGES.md | 1 + frontend/src/app/plugins/library.cljs | 33 ++++++++++++--------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2779dcc03c..e8fdc0b05d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -49,6 +49,7 @@ ### :bug: Bugs fixed - Fix plugin API `fileVersion.restore()` promise hanging indefinitely on restore failure [Github #9092](https://github.com/penpot/penpot/issues/9092) +- Fix plugin API `library.connectLibrary()` returning a non-Promise (or throwing synchronously) when the plugin lacks `library:write` permission — the method now always returns a `Promise` and rejects with a structured error message, matching the contract used by every other Promise-returning plugin method (`restore`, `remove`, `pin`, `saveVersion`, `findVersions`, …) - Fix LDAP provider params schema typo (`bind-passwor` → `bind-password`) introduced during the `clojure.spec` → `malli` migration; the schema slot now matches the runtime key actually read by `prepare-params` (`:password (:bind-password cfg)`) and `try-connectivity` (`(:bind-password cfg)`), so a wrong type for the password no longer slips through unvalidated - Fix `login-with-ldap` silently dropping its error message on the `ldap-not-initialized` restriction (typo `:hide` → `:hint`); the message `"ldap auth provider is not initialized"` now actually surfaces in logs and error responses instead of being discarded into an unread key - Fix `PENPOT_OIDC_USER_INFO_SOURCE` flag being silently ignored (`userinfo` / `token`) in the OIDC callback, causing "incomplete user info" failures during registration [Github #9108](https://github.com/penpot/penpot/issues/9108) diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index 1022a1a65c..8a249d32dc 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -1108,23 +1108,20 @@ :connectLibrary (fn [library-id] - (cond - (not (r/check-permission plugin-id "library:write")) - (u/not-valid plugin-id :connectLibrary "Plugin doesn't have 'library:write' permission") + (js/Promise. + (fn [resolve reject] + (cond + (not (r/check-permission plugin-id "library:write")) + (u/reject-not-valid reject :connectLibrary "Plugin doesn't have 'library:write' permission") - :else - (js/Promise. - (fn [resolve reject] - (cond - (not (string? library-id)) - (do (u/not-valid plugin-id :connectLibrary library-id) - (reject nil)) + (not (string? library-id)) + (u/reject-not-valid reject :connectLibrary library-id) - :else - (let [file-id (:current-file-id @st/state) - library-id (uuid/parse library-id)] - (->> st/stream - (rx/filter (ptk/type? ::dwl/attach-library-finished)) - (rx/take 1) - (rx/subs! #(resolve (library-proxy plugin-id library-id)) reject)) - (st/emit! (dwl/link-file-to-library file-id library-id)))))))))) + :else + (let [file-id (:current-file-id @st/state) + library-id (uuid/parse library-id)] + (->> st/stream + (rx/filter (ptk/type? ::dwl/attach-library-finished)) + (rx/take 1) + (rx/subs! #(resolve (library-proxy plugin-id library-id)) reject)) + (st/emit! (dwl/link-file-to-library file-id library-id)))))))))