🐛 Fix deleted files are accesible
Some checks failed
_STAGING / build-bundle (push) Has been cancelled
_STAGING / build-docker (push) Has been cancelled
_DEVELOP / build-bundle (push) Has been cancelled
_DEVELOP / build-docker (push) Has been cancelled
Commit Message Check / Check Commit Message (push) Has been cancelled

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?
[{:keys [deleted-at]}]
(and (ct/inst? deleted-at)
(< (inst-ms deleted-at)
(inst-ms (ct/now)))))
(some? deleted-at))
(defn get*
"Retrieve a single row from database that matches a simple filters. Do

View File

@@ -90,15 +90,16 @@
FROM snapshots AS c
WHERE c.id = ?
AND CASE WHEN c.created_by = 'user'
THEN (c.deleted_at IS NULL)
THEN c.deleted_at IS NULL
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"))
(defn get-minimal-snapshot
[cfg snapshot-id]
(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))))
(def ^:private sql:get-snapshot
@@ -115,7 +116,8 @@
"Get snapshot with decoded data"
[cfg file-id snapshot-id]
(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)
(fdata/resolve-file-data cfg)
(fdata/decode-file-data cfg))))

View File

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

View File

@@ -132,7 +132,6 @@
;; this will run pending task triggered by deleting user snapshot
(th/run-pending-tasks!)
;; this will
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
;; delete 2 snapshots and 2 file data entries
(t/is (= 4 (:processed res))))))))
@@ -179,7 +178,7 @@
(t/is (nil? (:error 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/testing "delete locked snapshot"

View File

@@ -117,29 +117,8 @@
(t/is (nil? (:error out)))
(t/is (nil? (:result out)))))
(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
::rpc/profile-id (:id prof)
:id file-id
@@ -959,9 +938,11 @@
:file-id (:id file)}
out (th/command! data)]
;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)]
(t/is (= 0 (count result)))))
(let [error (:error out)
error-data (ex-data error)]
(t/is (th/ex-info? error))
(t/is (= (:type error-data) :not-found))))
;; run permanent deletion (should be noop)
(let [result (th/run-task! :objects-gc {})]

View File

@@ -24,6 +24,7 @@
[app.main.data.workspace.layout :as dwly]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.texts :as dwt]
[app.main.router :as rt]
[app.util.globals :refer [global]]
[app.util.mouse :as mse]
[app.util.object :as obj]
@@ -38,6 +39,7 @@
(declare handle-presence)
(declare handle-pointer-update)
(declare handle-file-change)
(declare handle-file-deleted)
(declare handle-file-restore)
(declare handle-library-change)
(declare handle-pointer-send)
@@ -129,6 +131,7 @@
:disconnect (handle-presence msg)
:pointer-update (handle-pointer-update msg)
:file-change (handle-file-change msg)
:file-deleted (handle-file-deleted msg)
:file-restore (handle-file-restore msg)
:library-change (handle-library-change msg)
:notification (dc/handle-notification msg)
@@ -266,6 +269,18 @@
:redo-changes (vec 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
schema:handle-file-restore
[:map {:title "handle-file-restore"}