mirror of
https://github.com/penpot/penpot.git
synced 2025-05-11 10:06:37 +02:00
🐛 Fix files, projects and shared-files queries.
This commit is contained in:
parent
fc01690315
commit
5ae1b72943
4 changed files with 96 additions and 92 deletions
|
@ -273,20 +273,23 @@
|
||||||
and m.deleted_at is null) as graphics_count
|
and m.deleted_at is null) as graphics_count
|
||||||
from file as f
|
from file as f
|
||||||
left join page as pg on (f.id = pg.file_id)
|
left join page as pg on (f.id = pg.file_id)
|
||||||
where is_shared = true
|
inner join project as p on (p.id = f.project_id)
|
||||||
|
where f.is_shared = true
|
||||||
and f.deleted_at is null
|
and f.deleted_at is null
|
||||||
and pg.deleted_at is null
|
and pg.deleted_at is null
|
||||||
|
and p.deleted_at is null
|
||||||
|
and p.team_id = ?
|
||||||
window pages_w as (partition by f.id order by pg.ordering
|
window pages_w as (partition by f.id order by pg.ordering
|
||||||
range between unbounded preceding
|
range between unbounded preceding
|
||||||
and unbounded following)
|
and unbounded following)
|
||||||
order by f.modified_at desc")
|
order by f.modified_at desc")
|
||||||
|
|
||||||
(s/def ::shared-files
|
(s/def ::shared-files
|
||||||
(s/keys :req-un [::profile-id]))
|
(s/keys :req-un [::profile-id ::team-id]))
|
||||||
|
|
||||||
(sq/defquery ::shared-files
|
(sq/defquery ::shared-files
|
||||||
[{:keys [profile-id] :as params}]
|
[{:keys [profile-id team-id] :as params}]
|
||||||
(->> (db/exec! db/pool [sql:shared-files])
|
(->> (db/exec! db/pool [sql:shared-files team-id])
|
||||||
(mapv decode-row)))
|
(mapv decode-row)))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,91 +2,96 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.services.queries.projects
|
(ns app.services.queries.projects
|
||||||
(:require
|
(:require
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[promesa.core :as p]
|
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.exceptions :as ex]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.services.queries :as sq]
|
[app.services.queries :as sq]
|
||||||
[app.util.blob :as blob]))
|
[app.services.queries.teams :as teams]))
|
||||||
|
|
||||||
(declare decode-row)
|
;; --- Check Project Permissions
|
||||||
|
|
||||||
|
;; This SQL checks if the: (1) project is part of the team where the
|
||||||
|
;; profile has edition permissions or (2) the profile has direct
|
||||||
|
;; edition access granted to this project.
|
||||||
|
|
||||||
|
(def sql:project-permissions
|
||||||
|
"select tp.can_edit,
|
||||||
|
tp.is_admin,
|
||||||
|
tp.is_owner
|
||||||
|
from team_profile_rel as tp
|
||||||
|
where tp.profile_id = ?
|
||||||
|
and tp.team_id = ?
|
||||||
|
union
|
||||||
|
select pp.can_edit,
|
||||||
|
pp.is_admin,
|
||||||
|
pp.is_owner
|
||||||
|
from project_profile_rel as pp
|
||||||
|
where pp.profile_id = ?
|
||||||
|
and pp.project_id = ?;")
|
||||||
|
|
||||||
|
(defn check-edition-permissions!
|
||||||
|
[conn profile-id project]
|
||||||
|
(let [rows (db/exec! conn [sql:project-permissions
|
||||||
|
profile-id
|
||||||
|
(:team-id project)
|
||||||
|
profile-id
|
||||||
|
(:id project)])]
|
||||||
|
(when (empty? rows)
|
||||||
|
(ex/raise :type :not-found))
|
||||||
|
|
||||||
|
(when-not (or (some :can-edit rows)
|
||||||
|
(some :is-admin rows)
|
||||||
|
(some :is-owner rows))
|
||||||
|
(ex/raise :type :validation
|
||||||
|
:code :not-authorized))))
|
||||||
|
|
||||||
;; TODO: this module should be refactored for to separate the
|
|
||||||
;; permissions checks from the main queries in the same way as pages
|
|
||||||
;; and files. This refactor will make this functions more "reusable"
|
|
||||||
;; and will prevent duplicating queries on `queries.view` ns as
|
|
||||||
;; example.
|
|
||||||
|
|
||||||
;; --- Query: Projects
|
;; --- Query: Projects
|
||||||
|
|
||||||
(def ^:private sql:projects
|
(declare retrieve-projects)
|
||||||
"with projects as (
|
|
||||||
select p.*,
|
|
||||||
(select count(*) from file as f
|
|
||||||
where f.project_id = p.id
|
|
||||||
and deleted_at is null) as file_count
|
|
||||||
from project as p
|
|
||||||
inner join team_profile_rel as tpr on (tpr.team_id = p.team_id)
|
|
||||||
where tpr.profile_id = ?
|
|
||||||
and p.deleted_at is null
|
|
||||||
and (tpr.is_admin = true or
|
|
||||||
tpr.is_owner = true or
|
|
||||||
tpr.can_edit = true)
|
|
||||||
union
|
|
||||||
select p.*,
|
|
||||||
(select count(*) from file as f
|
|
||||||
where f.project_id = p.id
|
|
||||||
and deleted_at is null)
|
|
||||||
from project as p
|
|
||||||
inner join project_profile_rel as ppr on (ppr.project_id = p.id)
|
|
||||||
where ppr.profile_id = ?
|
|
||||||
and p.deleted_at is null
|
|
||||||
and (ppr.is_admin = true or
|
|
||||||
ppr.is_owner = true or
|
|
||||||
ppr.can_edit = true)
|
|
||||||
)
|
|
||||||
select *
|
|
||||||
from projects
|
|
||||||
where team_id = ?
|
|
||||||
order by modified_at desc")
|
|
||||||
|
|
||||||
(def ^:private sql:project-by-id
|
|
||||||
"select p.*
|
|
||||||
from project as p
|
|
||||||
inner join project_profile_rel as ppr on (ppr.project_id = p.id)
|
|
||||||
where ppr.profile_id = ?
|
|
||||||
and p.id = ?
|
|
||||||
and p.deleted_at is null
|
|
||||||
and (ppr.is_admin = true or
|
|
||||||
ppr.is_owner = true or
|
|
||||||
ppr.can_edit = true)")
|
|
||||||
|
|
||||||
(s/def ::team-id ::us/uuid)
|
(s/def ::team-id ::us/uuid)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::project-id ::us/uuid)
|
|
||||||
|
|
||||||
(s/def ::projects-by-team
|
(s/def ::projects
|
||||||
(s/keys :req-un [::profile-id ::team-id]))
|
(s/keys :req-un [::profile-id ::team-id]))
|
||||||
|
|
||||||
|
(sq/defquery ::projects
|
||||||
|
[{:keys [profile-id team-id]}]
|
||||||
|
(with-open [conn (db/open)]
|
||||||
|
(teams/check-read-permissions! conn profile-id team-id)
|
||||||
|
(retrieve-projects conn team-id)))
|
||||||
|
|
||||||
|
(def sql:projects
|
||||||
|
"select p.*,
|
||||||
|
(select count(*) from file as f
|
||||||
|
where f.project_id = p.id
|
||||||
|
and deleted_at is null)
|
||||||
|
from project as p
|
||||||
|
where team_id = ?
|
||||||
|
order by modified_at desc")
|
||||||
|
|
||||||
|
(defn retrieve-projects
|
||||||
|
[conn team-id]
|
||||||
|
(db/exec! conn [sql:projects team-id]))
|
||||||
|
|
||||||
|
;; --- Query: Projec by ID
|
||||||
|
|
||||||
|
(s/def ::project-id ::us/uuid)
|
||||||
(s/def ::project-by-id
|
(s/def ::project-by-id
|
||||||
(s/keys :req-un [::profile-id ::project-id]))
|
(s/keys :req-un [::profile-id ::project-id]))
|
||||||
|
|
||||||
(defn retrieve-projects
|
|
||||||
[conn profile-id team-id]
|
|
||||||
(db/exec! conn [sql:projects profile-id profile-id team-id]))
|
|
||||||
|
|
||||||
(defn retrieve-project
|
|
||||||
[conn profile-id id]
|
|
||||||
(db/exec-one! conn [sql:project-by-id profile-id id]))
|
|
||||||
|
|
||||||
(sq/defquery ::projects-by-team
|
|
||||||
[{:keys [profile-id team-id]}]
|
|
||||||
(retrieve-projects db/pool profile-id team-id))
|
|
||||||
|
|
||||||
(sq/defquery ::project-by-id
|
(sq/defquery ::project-by-id
|
||||||
[{:keys [profile-id project-id]}]
|
[{:keys [profile-id project-id]}]
|
||||||
(retrieve-project db/pool profile-id project-id))
|
(with-open [conn (db/open)]
|
||||||
|
(let [project (db/get-by-id conn :project project-id)]
|
||||||
|
(check-edition-permissions! conn profile-id project)
|
||||||
|
project)))
|
||||||
|
|
|
@ -14,24 +14,20 @@
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.services.queries :as sq]
|
[app.services.queries :as sq]
|
||||||
[app.services.queries.projects :refer [retrieve-projects]]
|
[app.services.queries.teams :as teams]
|
||||||
|
[app.services.queries.projects :as projects :refer [retrieve-projects]]
|
||||||
[app.services.queries.files :refer [decode-row]]))
|
[app.services.queries.files :refer [decode-row]]))
|
||||||
|
|
||||||
(def ^:private sql:project-files-recent
|
(def sql:project-recent-files
|
||||||
"select distinct
|
"select distinct
|
||||||
f.*,
|
f.*,
|
||||||
array_agg(pg.id) over pages_w as pages,
|
array_agg(pg.id) over pages_w as pages,
|
||||||
first_value(pg.data) over pages_w as data
|
first_value(pg.data) over pages_w as data
|
||||||
from file as f
|
from file as f
|
||||||
inner join file_profile_rel as fp_r on (fp_r.file_id = f.id)
|
|
||||||
left join page as pg on (f.id = pg.file_id)
|
left join page as pg on (f.id = pg.file_id)
|
||||||
where fp_r.profile_id = ?
|
where f.project_id = ?
|
||||||
and f.project_id = ?
|
|
||||||
and f.deleted_at is null
|
and f.deleted_at is null
|
||||||
and pg.deleted_at is null
|
and pg.deleted_at is null
|
||||||
and (fp_r.is_admin = true or
|
|
||||||
fp_r.is_owner = true or
|
|
||||||
fp_r.can_edit = true)
|
|
||||||
window pages_w as (partition by f.id order by pg.ordering
|
window pages_w as (partition by f.id order by pg.ordering
|
||||||
range between unbounded preceding
|
range between unbounded preceding
|
||||||
and unbounded following)
|
and unbounded following)
|
||||||
|
@ -39,10 +35,11 @@
|
||||||
limit 5")
|
limit 5")
|
||||||
|
|
||||||
(defn recent-by-project
|
(defn recent-by-project
|
||||||
[profile-id project]
|
[conn profile-id project]
|
||||||
(let [project-id (:id project)]
|
(let [project-id (:id project)]
|
||||||
(->> (db/exec! db/pool [sql:project-files-recent profile-id project-id])
|
(projects/check-edition-permissions! conn profile-id project)
|
||||||
(mapv decode-row))))
|
(->> (db/exec! conn [sql:project-recent-files project-id])
|
||||||
|
(map decode-row))))
|
||||||
|
|
||||||
(s/def ::team-id ::us/uuid)
|
(s/def ::team-id ::us/uuid)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
|
@ -52,9 +49,11 @@
|
||||||
|
|
||||||
(sq/defquery ::recent-files
|
(sq/defquery ::recent-files
|
||||||
[{:keys [profile-id team-id]}]
|
[{:keys [profile-id team-id]}]
|
||||||
(->> (retrieve-projects db/pool profile-id team-id)
|
(with-open [conn (db/open)]
|
||||||
;; Retrieve for each proyect the 5 more recent files
|
(teams/check-read-permissions! conn profile-id team-id)
|
||||||
(map (partial recent-by-project profile-id))
|
(->> (retrieve-projects conn team-id)
|
||||||
;; Change the structure so it's a map with project-id as keys
|
;; Retrieve for each proyect the 5 more recent files
|
||||||
(flatten)
|
(map (partial recent-by-project conn profile-id))
|
||||||
(group-by :project-id)))
|
;; Change the structure so it's a map with project-id as keys
|
||||||
|
(flatten)
|
||||||
|
(group-by :project-id))))
|
||||||
|
|
|
@ -30,8 +30,7 @@
|
||||||
(defn check-edition-permissions!
|
(defn check-edition-permissions!
|
||||||
[conn profile-id team-id]
|
[conn profile-id team-id]
|
||||||
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
||||||
(when-not (or (= team-id uuid/zero) ;; We can write global-project owned items
|
(when-not (or (:can-edit row)
|
||||||
(:can-edit row)
|
|
||||||
(:is-admin row)
|
(:is-admin row)
|
||||||
(:is-owner row))
|
(:is-owner row))
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
|
@ -40,9 +39,7 @@
|
||||||
(defn check-read-permissions!
|
(defn check-read-permissions!
|
||||||
[conn profile-id team-id]
|
[conn profile-id team-id]
|
||||||
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
(let [row (db/exec-one! conn [sql:team-permissions profile-id team-id])]
|
||||||
(when-not (or (= team-id uuid/zero) ;; We can read global-project owned items
|
;; when row is found this means that read permission is granted.
|
||||||
(:can-edit row)
|
(when-not row
|
||||||
(:is-admin row)
|
|
||||||
(:is-owner row))
|
|
||||||
(ex/raise :type :validation
|
(ex/raise :type :validation
|
||||||
:code :not-authorized))))
|
:code :not-authorized))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue