🐛 Fix deleted files are accesible

This commit is contained in:
Alejandro Alonso 2025-10-08 12:55:52 +02:00 committed by Andrey Antukh
parent 5717708b56
commit 2b7bd8fa5c
6 changed files with 46 additions and 39 deletions

View File

@ -379,9 +379,7 @@
(defn is-row-deleted? (defn is-row-deleted?
[{:keys [deleted-at]}] [{:keys [deleted-at]}]
(and (ct/inst? deleted-at) (some? deleted-at))
(< (inst-ms deleted-at)
(inst-ms (ct/now)))))
(defn get* (defn get*
"Retrieve a single row from database that matches a simple filters. Do "Retrieve a single row from database that matches a simple filters. Do

View File

@ -90,15 +90,16 @@
FROM snapshots AS c FROM snapshots AS c
WHERE c.id = ? WHERE c.id = ?
AND CASE WHEN c.created_by = 'user' AND CASE WHEN c.created_by = 'user'
THEN (c.deleted_at IS NULL) THEN c.deleted_at IS NULL
WHEN c.created_by = 'system' WHEN c.created_by = 'system'
THEN (c.deleted_at IS NULL OR c.deleted_at >= ?::timestamptz) THEN c.deleted_at IS NULL OR c.deleted_at >= ?::timestamptz
END")) END"))
(defn get-minimal-snapshot (defn get-minimal-snapshot
[cfg snapshot-id] [cfg snapshot-id]
(let [now (ct/now)] (let [now (ct/now)]
(-> (db/get-with-sql cfg [sql:get-snapshot-without-data snapshot-id now]) (-> (db/get-with-sql cfg [sql:get-snapshot-without-data snapshot-id now]
{::db/remove-deleted false})
(decode-snapshot)))) (decode-snapshot))))
(def ^:private sql:get-snapshot (def ^:private sql:get-snapshot
@ -115,7 +116,8 @@
"Get snapshot with decoded data" "Get snapshot with decoded data"
[cfg file-id snapshot-id] [cfg file-id snapshot-id]
(let [now (ct/now)] (let [now (ct/now)]
(->> (db/get-with-sql cfg [sql:get-snapshot file-id snapshot-id now]) (->> (db/get-with-sql cfg [sql:get-snapshot file-id snapshot-id now]
{::db/remove-deleted false})
(decode-snapshot) (decode-snapshot)
(fdata/resolve-file-data cfg) (fdata/resolve-file-data cfg)
(fdata/decode-file-data cfg)))) (fdata/decode-file-data cfg))))

View File

@ -27,6 +27,7 @@
[app.features.logical-deletion :as ldel] [app.features.logical-deletion :as ldel]
[app.loggers.audit :as-alias audit] [app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as-alias webhooks] [app.loggers.webhooks :as-alias webhooks]
[app.msgbus :as mbus]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.commands.projects :as projects] [app.rpc.commands.projects :as projects]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
@ -80,8 +81,10 @@
fpr.is_admin, fpr.is_admin,
fpr.can_edit fpr.can_edit
from file_profile_rel as fpr from file_profile_rel as fpr
inner join file as f on (f.id = fpr.file_id)
where fpr.file_id = ? where fpr.file_id = ?
and fpr.profile_id = ? and fpr.profile_id = ?
and f.deleted_at is null
union all union all
select tpr.is_owner, select tpr.is_owner,
tpr.is_admin, tpr.is_admin,
@ -91,6 +94,7 @@
inner join file as f on (p.id = f.project_id) inner join file as f on (p.id = f.project_id)
where f.id = ? where f.id = ?
and tpr.profile_id = ? and tpr.profile_id = ?
and f.deleted_at is null
union all union all
select ppr.is_owner, select ppr.is_owner,
ppr.is_admin, ppr.is_admin,
@ -98,7 +102,8 @@
from project_profile_rel as ppr from project_profile_rel as ppr
inner join file as f on (f.project_id = ppr.project_id) inner join file as f on (f.project_id = ppr.project_id)
where f.id = ? where f.id = ?
and ppr.profile_id = ?") and ppr.profile_id = ?
and f.deleted_at is null")
(defn get-file-permissions (defn get-file-permissions
[conn profile-id file-id] [conn profile-id file-id]
@ -730,9 +735,9 @@
(defn- get-file-info (defn- get-file-info
[{:keys [::db/conn] :as cfg} {:keys [id] :as params}] [{:keys [::db/conn] :as cfg} {:keys [id] :as params}]
(db/get* conn :file (db/get conn :file
{:id id} {:id id}
{::sql/columns [:id]})) {::sql/columns [:id :deleted-at]}))
(sv/defmethod ::get-file-info (sv/defmethod ::get-file-info
"Retrieve minimal file info by its ID." "Retrieve minimal file info by its ID."
@ -944,7 +949,14 @@
(let [team (teams/get-team conn (let [team (teams/get-team conn
:profile-id profile-id :profile-id profile-id
:file-id id) :file-id id)
file (mark-file-deleted conn team id)] file (mark-file-deleted conn team id)
msgbus (::mbus/msgbus cfg)]
(mbus/pub! msgbus
:topic id
:message {:type :file-deleted
:file-id id
:profile-id profile-id})
(rph/with-meta (rph/wrap) (rph/with-meta (rph/wrap)
{::audit/props {:project-id (:project-id file) {::audit/props {:project-id (:project-id file)

View File

@ -132,7 +132,6 @@
;; this will run pending task triggered by deleting user snapshot ;; this will run pending task triggered by deleting user snapshot
(th/run-pending-tasks!) (th/run-pending-tasks!)
;; this will
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})] (let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
;; delete 2 snapshots and 2 file data entries ;; delete 2 snapshots and 2 file data entries
(t/is (= 4 (:processed res)))))))) (t/is (= 4 (:processed res))))))))
@ -179,7 +178,7 @@
(t/is (nil? (:error out))) (t/is (nil? (:error out)))
(t/is (true? (:result out))) (t/is (true? (:result out)))
(let [snapshot (th/db-get :file-change {:id (:id snapshot)})] (let [snapshot (th/db-get :file-change {:id (:id snapshot)} {::db/remove-deleted false})]
(t/is (= (:id profile-1) (:locked-by snapshot)))))) (t/is (= (:id profile-1) (:locked-by snapshot))))))
(t/testing "delete locked snapshot" (t/testing "delete locked snapshot"

View File

@ -117,29 +117,8 @@
(t/is (nil? (:error out))) (t/is (nil? (:error out)))
(t/is (nil? (:result out))))) (t/is (nil? (:result out)))))
(t/testing "query single file after delete" (t/testing "query single file after delete"
(let [data {::th/type :get-file
::rpc/profile-id (:id prof)
:id file-id
:components-v2 true}
out (th/command! data)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)]
(t/is (some? (:deleted-at result)))
(t/is (= file-id (:id result)))
(t/is (= "new name" (:name result)))
(t/is (= 1 (count (get-in result [:data :pages]))))
(t/is (nil? (:users result))))))
(th/db-update! :file
{:deleted-at (ct/now)}
{:id file-id})
(t/testing "query single file after delete and wait"
(let [data {::th/type :get-file (let [data {::th/type :get-file
::rpc/profile-id (:id prof) ::rpc/profile-id (:id prof)
:id file-id :id file-id
@ -959,9 +938,11 @@
:file-id (:id file)} :file-id (:id file)}
out (th/command! data)] out (th/command! data)]
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)] (let [error (:error out)
(t/is (= 0 (count result))))) error-data (ex-data error)]
(t/is (th/ex-info? error))
(t/is (= (:type error-data) :not-found))))
;; run permanent deletion (should be noop) ;; run permanent deletion (should be noop)
(let [result (th/run-task! :objects-gc {})] (let [result (th/run-task! :objects-gc {})]

View File

@ -24,6 +24,7 @@
[app.main.data.workspace.layout :as dwly] [app.main.data.workspace.layout :as dwly]
[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.router :as rt]
[app.util.globals :refer [global]] [app.util.globals :refer [global]]
[app.util.mouse :as mse] [app.util.mouse :as mse]
[app.util.object :as obj] [app.util.object :as obj]
@ -38,6 +39,7 @@
(declare handle-presence) (declare handle-presence)
(declare handle-pointer-update) (declare handle-pointer-update)
(declare handle-file-change) (declare handle-file-change)
(declare handle-file-deleted)
(declare handle-file-restore) (declare handle-file-restore)
(declare handle-library-change) (declare handle-library-change)
(declare handle-pointer-send) (declare handle-pointer-send)
@ -129,6 +131,7 @@
:disconnect (handle-presence msg) :disconnect (handle-presence msg)
:pointer-update (handle-pointer-update msg) :pointer-update (handle-pointer-update msg)
:file-change (handle-file-change msg) :file-change (handle-file-change msg)
:file-deleted (handle-file-deleted msg)
:file-restore (handle-file-restore msg) :file-restore (handle-file-restore msg)
:library-change (handle-library-change msg) :library-change (handle-library-change msg)
:notification (dc/handle-notification msg) :notification (dc/handle-notification msg)
@ -266,6 +269,18 @@
:redo-changes (vec changes) :redo-changes (vec changes)
:undo-changes []}))))) :undo-changes []})))))
(defn handle-file-deleted
[{:keys [file-id] :as msg}]
(ptk/reify ::handle-file-deleted
ptk/WatchEvent
(watch [_ state _]
(let [curr-file-id (:current-file-id state)
team-id (:current-team-id state)]
;; If the deleted file is the currently open one
(when (= file-id curr-file-id)
(rx/of
(rt/nav :dashboard-recent {:team-id team-id})))))))
(def ^:private (def ^:private
schema:handle-file-restore schema:handle-file-restore
[:map {:title "handle-file-restore"} [:map {:title "handle-file-restore"}