♻️ Add admin facilities on the code base

- Fix bugs related to orphan teams on profile deletion
- Separate session based profile-id param from api user provided
This commit is contained in:
Andrey Antukh 2022-12-21 11:53:56 +01:00
parent 53d9b547c3
commit b929564fa7
53 changed files with 872 additions and 468 deletions

26
backend/src/app/auth.clj Normal file
View file

@ -0,0 +1,26 @@
;; 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
(ns app.auth
(:require
[buddy.hashers :as hashers]))
(defn derive-password
[password]
(hashers/derive password
{:alg :argon2id
:memory 16384
:iterations 20
:parallelism 2}))
(defn verify-password
[attempt password]
(try
(hashers/verify attempt password)
(catch Throwable _
{:update false
:valid false})))

View file

@ -106,6 +106,9 @@
(s/def ::file-change-snapshot-every ::us/integer) (s/def ::file-change-snapshot-every ::us/integer)
(s/def ::file-change-snapshot-timeout ::dt/duration) (s/def ::file-change-snapshot-timeout ::dt/duration)
(s/def ::setup-admin-email ::us/email)
(s/def ::setup-admin-password ::us/not-empty-string)
(s/def ::default-executor-parallelism ::us/integer) (s/def ::default-executor-parallelism ::us/integer)
(s/def ::scheduled-executor-parallelism ::us/integer) (s/def ::scheduled-executor-parallelism ::us/integer)
@ -295,6 +298,9 @@
::srepl-host ::srepl-host
::srepl-port ::srepl-port
::setup-admin-email
::setup-admin-password
::assets-storage-backend ::assets-storage-backend
::storage-assets-fs-directory ::storage-assets-fs-directory
::storage-assets-s3-bucket ::storage-assets-s3-bucket

View file

@ -290,7 +290,11 @@
{:pool (ig/ref ::db/pool) {:pool (ig/ref ::db/pool)
:executor (ig/ref ::wrk/executor) :executor (ig/ref ::wrk/executor)
:storage (ig/ref ::sto/storage) :storage (ig/ref ::sto/storage)
:session (ig/ref :app.http.session/manager)} :session (ig/ref :app.http.session/manager)
::db/pool (ig/ref ::db/pool)
::wrk/executor (ig/ref ::wrk/executor)
::sto/storage (ig/ref ::sto/storage)}
:app.http.websocket/handler :app.http.websocket/handler
{:pool (ig/ref ::db/pool) {:pool (ig/ref ::db/pool)
@ -385,8 +389,8 @@
:max-age cf/deletion-delay} :max-age cf/deletion-delay}
:app.tasks.objects-gc/handler :app.tasks.objects-gc/handler
{:pool (ig/ref ::db/pool) {::db/pool (ig/ref ::db/pool)
:storage (ig/ref ::sto/storage)} ::sto/storage (ig/ref ::sto/storage)}
:app.tasks.file-gc/handler :app.tasks.file-gc/handler
{:pool (ig/ref ::db/pool)} {:pool (ig/ref ::db/pool)}
@ -403,6 +407,9 @@
{:port (cf/get :srepl-port) {:port (cf/get :srepl-port)
:host (cf/get :srepl-host)} :host (cf/get :srepl-host)}
:app.setup/initial-profile
{::db/pool (ig/ref ::db/pool)}
:app.setup/builtin-templates :app.setup/builtin-templates
{::http.client/client (ig/ref ::http.client/client)} {::http.client/client (ig/ref ::http.client/client)}

View file

@ -271,6 +271,37 @@
{:name "0087-mod-task-table" {:name "0087-mod-task-table"
:fn (mg/resource "app/migrations/sql/0087-mod-task-table.sql")} :fn (mg/resource "app/migrations/sql/0087-mod-task-table.sql")}
{:name "0088-mod-team-profile-rel-table"
:fn (mg/resource "app/migrations/sql/0088-mod-team-profile-rel-table.sql")}
{:name "0089-mod-project-profile-rel-table"
:fn (mg/resource "app/migrations/sql/0089-mod-project-profile-rel-table.sql")}
{:name "0090-mod-http-session-table"
:fn (mg/resource "app/migrations/sql/0090-mod-http-session-table.sql")}
{:name "0091-mod-team-project-profile-rel-table"
:fn (mg/resource "app/migrations/sql/0091-mod-team-project-profile-rel-table.sql")}
{:name "0092-mod-team-invitation-table"
:fn (mg/resource "app/migrations/sql/0092-mod-team-invitation-table.sql")}
{:name "0093-del-file-share-tokens-table"
:fn (mg/resource "app/migrations/sql/0093-del-file-share-tokens-table.sql")}
{:name "0094-del-profile-attr-table"
:fn (mg/resource "app/migrations/sql/0094-del-profile-attr-table.sql")}
{:name "0095-del-storage-data-table"
:fn (mg/resource "app/migrations/sql/0095-del-storage-data-table.sql")}
{:name "0096-del-storage-pending-table"
:fn (mg/resource "app/migrations/sql/0096-del-storage-pending-table.sql")}
{:name "0097-mod-profile-table"
:fn (mg/resource "app/migrations/sql/0097-mod-profile-table.sql")}
]) ])

View file

@ -0,0 +1,3 @@
ALTER TABLE team_profile_rel DROP CONSTRAINT team_profile_rel_pkey;
ALTER TABLE team_profile_rel ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
ALTER TABLE team_profile_rel ADD CONSTRAINT team_profile_rel_unique UNIQUE (team_id, profile_id);

View file

@ -0,0 +1,3 @@
ALTER TABLE project_profile_rel DROP CONSTRAINT project_profile_rel_pkey;
ALTER TABLE project_profile_rel ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
ALTER TABLE project_profile_rel ADD CONSTRAINT project_profile_rel_unique UNIQUE (project_id, profile_id);

View file

@ -0,0 +1,2 @@
ALTER TABLE http_session DROP CONSTRAINT http_session_pkey;
ALTER TABLE http_session ADD CONSTRAINT http_session_pkey PRIMARY KEY (id);

View file

@ -0,0 +1,3 @@
ALTER TABLE team_project_profile_rel DROP CONSTRAINT team_project_profile_rel_pkey;
ALTER TABLE team_project_profile_rel ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
ALTER TABLE team_project_profile_rel ADD CONSTRAINT team_project_profile_rel_unique UNIQUE (team_id, project_id, profile_id);

View file

@ -0,0 +1,3 @@
ALTER TABLE team_invitation DROP CONSTRAINT team_invitation_pkey;
ALTER TABLE team_invitation ADD COLUMN id uuid DEFAULT uuid_generate_v4() PRIMARY KEY;
ALTER TABLE team_invitation ADD CONSTRAINT team_invitation_unique UNIQUE (team_id, email_to);

View file

@ -0,0 +1 @@
DROP TABLE file_share_token;

View file

@ -0,0 +1 @@
DROP TABLE profile_attr;

View file

@ -0,0 +1 @@
DROP TABLE storage_data;

View file

@ -0,0 +1 @@
DROP TABLE storage_pending;

View file

@ -0,0 +1,2 @@
ALTER TABLE profile
ADD COLUMN is_admin boolean DEFAULT false;

View file

@ -35,6 +35,8 @@
[yetti.request :as yrq] [yetti.request :as yrq]
[yetti.response :as yrs])) [yetti.response :as yrs]))
(s/def ::profile-id ::us/uuid)
(defn- default-handler (defn- default-handler
[_] [_]
(p/rejected (ex/error :type :not-found))) (p/rejected (ex/error :type :not-found)))
@ -72,8 +74,11 @@
(let [type (keyword (:type params)) (let [type (keyword (:type params))
data (into {::http/request request} params) data (into {::http/request request} params)
data (if profile-id data (if profile-id
(assoc data :profile-id profile-id ::session-id session-id) (assoc data
(dissoc data :profile-id)) :profile-id profile-id
::profile-id profile-id
::session-id session-id)
(dissoc data :profile-id ::profile-id))
method (get methods type default-handler)] method (get methods type default-handler)]
(-> (method data) (-> (method data)
@ -90,8 +95,11 @@
(let [type (keyword (:type params)) (let [type (keyword (:type params))
data (into {::http/request request} params) data (into {::http/request request} params)
data (if profile-id data (if profile-id
(assoc data :profile-id profile-id ::session-id session-id) (assoc data
(dissoc data :profile-id)) :profile-id profile-id
::profile-id profile-id
::session-id session-id)
(dissoc data :profile-id ::profile-id))
method (get methods type default-handler)] method (get methods type default-handler)]
(-> (method data) (-> (method data)
@ -109,9 +117,8 @@
etag (yrq/get-header request "if-none-match") etag (yrq/get-header request "if-none-match")
data (into {::http/request request ::cond/key etag} params) data (into {::http/request request ::cond/key etag} params)
data (if profile-id data (if profile-id
(assoc data :profile-id profile-id ::session-id session-id) (assoc data ::profile-id profile-id ::session-id session-id)
(dissoc data :profile-id)) (dissoc data ::profile-id))
method (get methods cmd default-handler)] method (get methods cmd default-handler)]
(binding [cond/*enabled* true] (binding [cond/*enabled* true]
(-> (method data) (-> (method data)
@ -152,9 +159,12 @@
(letfn [(handle-audit [params result] (letfn [(handle-audit [params result]
(let [resultm (meta result) (let [resultm (meta result)
request (::http/request params) request (::http/request params)
profile-id (or (::audit/profile-id resultm) profile-id (or (::audit/profile-id resultm)
(:profile-id result) (:profile-id result)
(:profile-id params) (if (= (::type cfg) "command")
(::profile-id params)
(:profile-id params))
uuid/zero) uuid/zero)
props (-> (or (::audit/replace-props resultm) props (-> (or (::audit/replace-props resultm)
@ -209,21 +219,24 @@
(wrap-audit cfg $ mdata)) (wrap-audit cfg $ mdata))
spec (or (::sv/spec mdata) (s/spec any?)) spec (or (::sv/spec mdata) (s/spec any?))
auth? (:auth mdata true)] auth? (::auth mdata true)]
(l/debug :hint "register method" :name (::sv/name mdata)) (l/debug :hint "register method" :name (::sv/name mdata))
(with-meta (with-meta
(fn [params] (fn [params]
;; Raise authentication error when rpc method requires auth but ;; Raise authentication error when rpc method requires auth but
;; no profile-id is found in the request. ;; no profile-id is found in the request.
(let [profile-id (if (= "command" (::type cfg))
(::profile-id params)
(:profile-id params))]
(p/do! (p/do!
(if (and auth? (not (uuid? (:profile-id params)))) (if (and auth? (not (uuid? profile-id)))
(ex/raise :type :authentication (ex/raise :type :authentication
:code :authentication-required :code :authentication-required
:hint "authentication required for this endpoint") :hint "authentication required for this endpoint")
(let [params (us/conform spec params)] (let [params (us/conform spec params)]
(f cfg params))))) (f cfg params))))))
mdata))) mdata)))
(defn- process-method (defn- process-method
@ -262,6 +275,7 @@
(let [cfg (assoc cfg ::type "command" ::metrics-id :rpc-command-timing)] (let [cfg (assoc cfg ::type "command" ::metrics-id :rpc-command-timing)]
(->> (sv/scan-ns 'app.rpc.commands.binfile (->> (sv/scan-ns 'app.rpc.commands.binfile
'app.rpc.commands.comments 'app.rpc.commands.comments
'app.rpc.commands.profile
'app.rpc.commands.management 'app.rpc.commands.management
'app.rpc.commands.verify-token 'app.rpc.commands.verify-token
'app.rpc.commands.search 'app.rpc.commands.search

View file

@ -15,6 +15,7 @@
[app.db :as db] [app.db :as db]
[app.http :as-alias http] [app.http :as-alias http]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.rpc :as-alias rpc]
[app.rpc.climit :as-alias climit] [app.rpc.climit :as-alias climit]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
@ -41,7 +42,7 @@
:profile-id :ip-addr :props :context]) :profile-id :ip-addr :props :context])
(defn- handle-events (defn- handle-events
[{:keys [::db/pool]} {:keys [profile-id events ::http/request] :as params}] [{:keys [::db/pool]} {:keys [::rpc/profile-id events ::http/request] :as params}]
(let [ip-addr (audit/parse-client-ip request) (let [ip-addr (audit/parse-client-ip request)
xform (comp xform (comp
(map #(assoc % :profile-id profile-id)) (map #(assoc % :profile-id profile-id))
@ -53,7 +54,6 @@
(when (seq events) (when (seq events)
(db/insert-multi! pool :audit-log event-columns events)))) (db/insert-multi! pool :audit-log event-columns events))))
(s/def ::profile-id ::us/uuid)
(s/def ::name ::us/string) (s/def ::name ::us/string)
(s/def ::type ::us/string) (s/def ::type ::us/string)
(s/def ::props (s/map-of ::us/keyword any?)) (s/def ::props (s/map-of ::us/keyword any?))
@ -67,7 +67,8 @@
(s/def ::events (s/every ::event)) (s/def ::events (s/every ::event))
(s/def ::push-audit-events (s/def ::push-audit-events
(s/keys :req-un [::events ::profile-id])) (s/keys :req [::rpc/profile-id]
:req-un [::events]))
(sv/defmethod ::push-audit-events (sv/defmethod ::push-audit-events
{::climit/queue :push-audit-events {::climit/queue :push-audit-events

View file

@ -6,6 +6,7 @@
(ns app.rpc.commands.auth (ns app.rpc.commands.auth
(:require (:require
[app.auth :as auth]
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.spec :as us] [app.common.spec :as us]
@ -15,6 +16,8 @@
[app.emails :as eml] [app.emails :as eml]
[app.http.session :as session] [app.http.session :as session]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.main :as-alias main]
[app.rpc :as-alias rpc]
[app.rpc.climit :as climit] [app.rpc.climit :as climit]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
@ -23,7 +26,6 @@
[app.tokens :as tokens] [app.tokens :as tokens]
[app.util.services :as sv] [app.util.services :as sv]
[app.util.time :as dt] [app.util.time :as dt]
[buddy.hashers :as hashers]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -31,7 +33,6 @@
(s/def ::fullname ::us/not-empty-string) (s/def ::fullname ::us/not-empty-string)
(s/def ::lang ::us/string) (s/def ::lang ::us/string)
(s/def ::path ::us/string) (s/def ::path ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::password ::us/not-empty-string) (s/def ::password ::us/not-empty-string)
(s/def ::old-password ::us/not-empty-string) (s/def ::old-password ::us/not-empty-string)
(s/def ::theme ::us/string) (s/def ::theme ::us/string)
@ -40,22 +41,6 @@
;; ---- HELPERS ;; ---- HELPERS
(defn derive-password
[password]
(hashers/derive password
{:alg :argon2id
:memory 16384
:iterations 20
:parallelism 2}))
(defn verify-password
[attempt password]
(try
(hashers/verify attempt password)
(catch Exception _e
{:update false
:valid false})))
(defn email-domain-in-whitelist? (defn email-domain-in-whitelist?
"Returns true if email's domain is in the given whitelist or if "Returns true if email's domain is in the given whitelist or if
given whitelist is an empty string." given whitelist is an empty string."
@ -84,7 +69,7 @@
;; ---- COMMAND: login with password ;; ---- COMMAND: login with password
(defn login-with-password (defn login-with-password
[{:keys [pool session sprops] :as cfg} {:keys [email password] :as params}] [{:keys [::db/pool session] :as cfg} {:keys [email password scope] :as params}]
(when-not (contains? cf/flags :login) (when-not (contains? cf/flags :login)
(ex/raise :type :restriction (ex/raise :type :restriction
@ -96,7 +81,7 @@
(ex/raise :type :validation (ex/raise :type :validation
:code :account-without-password :code :account-without-password
:hint "the current account does not have password")) :hint "the current account does not have password"))
(:valid (verify-password password (:password profile)))) (:valid (auth/verify-password password (:password profile))))
(validate-profile [profile] (validate-profile [profile]
(when-not profile (when-not profile
@ -126,27 +111,37 @@
(profile/decode-profile-row)) (profile/decode-profile-row))
invitation (when-let [token (:invitation-token params)] invitation (when-let [token (:invitation-token params)]
(tokens/verify sprops {:token token :iss :team-invitation})) (tokens/verify (::main/props cfg) {:token token :iss :team-invitation}))
;; If invitation member-id does not matches the profile-id, we just proceed to ignore the ;; If invitation member-id does not matches the profile-id, we just proceed to ignore the
;; invitation because invitations matches exactly; and user can't login with other email and ;; invitation because invitations matches exactly; and user can't login with other email and
;; accept invitation with other email ;; accept invitation with other email
response (if (and (some? invitation) (= (:id profile) (:member-id invitation))) response (if (and (some? invitation) (= (:id profile) (:member-id invitation)))
{:invitation-token (:invitation-token params)} {:invitation-token (:invitation-token params)}
profile)] (update profile :is-admin (fn [admin?]
(or admin?
(let [admins (cf/get :admins)]
(contains? admins (:email profile)))))))]
(when (and (nil? (:default-team-id profile))
(not= scope "admin"))
(ex/raise :type :restriction
:code :admin-only-profile
:hint "can't login with admin-only profile"))
(-> response (-> response
(rph/with-transform (session/create-fn session (:id profile))) (rph/with-transform (session/create-fn session (:id profile)))
(rph/with-meta {::audit/props (audit/profile->props profile) (rph/with-meta {::audit/props (audit/profile->props profile)
::audit/profile-id (:id profile)})))))) ::audit/profile-id (:id profile)}))))))
(s/def ::scope ::us/string)
(s/def ::login-with-password (s/def ::login-with-password
(s/keys :req-un [::email ::password] (s/keys :req-un [::email ::password]
:opt-un [::invitation-token])) :opt-un [::invitation-token ::scope]))
(sv/defmethod ::login-with-password (sv/defmethod ::login-with-password
"Performs authentication using penpot password." "Performs authentication using penpot password."
{:auth false {::rpc/auth false
::climit/queue :auth ::climit/queue :auth
::doc/added "1.15"} ::doc/added "1.15"}
[cfg params] [cfg params]
@ -155,11 +150,11 @@
;; ---- COMMAND: Logout ;; ---- COMMAND: Logout
(s/def ::logout (s/def ::logout
(s/keys :opt-un [::profile-id])) (s/keys :opt [::rpc/profile-id]))
(sv/defmethod ::logout (sv/defmethod ::logout
"Clears the authentication cookie and logout the current session." "Clears the authentication cookie and logout the current session."
{:auth false {::rpc/auth false
::doc/added "1.15"} ::doc/added "1.15"}
[{:keys [session] :as cfg} _] [{:keys [session] :as cfg} _]
(rph/with-transform {} (session/delete-fn session))) (rph/with-transform {} (session/delete-fn session)))
@ -167,13 +162,13 @@
;; ---- COMMAND: Recover Profile ;; ---- COMMAND: Recover Profile
(defn recover-profile (defn recover-profile
[{:keys [pool sprops] :as cfg} {:keys [token password]}] [{:keys [::db/pool] :as cfg} {:keys [token password]}]
(letfn [(validate-token [token] (letfn [(validate-token [token]
(let [tdata (tokens/verify sprops {:token token :iss :password-recovery})] (let [tdata (tokens/verify (::main/props cfg) {:token token :iss :password-recovery})]
(:profile-id tdata))) (:profile-id tdata)))
(update-password [conn profile-id] (update-password [conn profile-id]
(let [pwd (derive-password password)] (let [pwd (auth/derive-password password)]
(db/update! conn :profile {:password pwd} {:id profile-id})))] (db/update! conn :profile {:password pwd} {:id profile-id})))]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
@ -186,7 +181,7 @@
(s/keys :req-un [::token ::password])) (s/keys :req-un [::token ::password]))
(sv/defmethod ::recover-profile (sv/defmethod ::recover-profile
{:auth false {::rpc/auth false
::climit/queue :auth ::climit/queue :auth
::doc/added "1.15"} ::doc/added "1.15"}
[cfg params] [cfg params]
@ -195,13 +190,13 @@
;; ---- COMMAND: Prepare Register ;; ---- COMMAND: Prepare Register
(defn validate-register-attempt! (defn validate-register-attempt!
[{:keys [pool sprops]} params] [{:keys [::db/pool] :as cfg} params]
(when-not (contains? cf/flags :registration) (when-not (contains? cf/flags :registration)
(if-not (contains? params :invitation-token) (if-not (contains? params :invitation-token)
(ex/raise :type :restriction (ex/raise :type :restriction
:code :registration-disabled) :code :registration-disabled)
(let [invitation (tokens/verify sprops {:token (:invitation-token params) :iss :team-invitation})] (let [invitation (tokens/verify (::main/props cfg) {:token (:invitation-token params) :iss :team-invitation})]
(when-not (= (:email params) (:member-email invitation)) (when-not (= (:email params) (:member-email invitation))
(ex/raise :type :restriction (ex/raise :type :restriction
:code :email-does-not-match-invitation :code :email-does-not-match-invitation
@ -235,7 +230,7 @@
(pos? (compare elapsed register-retry-threshold)))) (pos? (compare elapsed register-retry-threshold))))
(defn prepare-register (defn prepare-register
[{:keys [pool sprops] :as cfg} params] [{:keys [::db/pool] :as cfg} params]
(validate-register-attempt! cfg params) (validate-register-attempt! cfg params)
@ -264,7 +259,7 @@
params (d/without-nils params) params (d/without-nils params)
token (tokens/generate sprops params)] token (tokens/generate (::main/props cfg) params)]
(with-meta {:token token} (with-meta {:token token}
{::audit/profile-id uuid/zero}))) {::audit/profile-id uuid/zero})))
@ -273,7 +268,7 @@
:opt-un [::invitation-token])) :opt-un [::invitation-token]))
(sv/defmethod ::prepare-register-profile (sv/defmethod ::prepare-register-profile
{:auth false {::rpc/auth false
::doc/added "1.15"} ::doc/added "1.15"}
[cfg params] [cfg params]
(prepare-register cfg params)) (prepare-register cfg params))
@ -293,7 +288,7 @@
(db/tjson)) (db/tjson))
password (if-let [password (:password params)] password (if-let [password (:password params)]
(derive-password password) (auth/derive-password password)
"!") "!")
locale (:locale params) locale (:locale params)
@ -339,15 +334,15 @@
(assoc :default-project-id (:default-project-id team))))) (assoc :default-project-id (:default-project-id team)))))
(defn send-email-verification! (defn send-email-verification!
[conn sprops profile] [conn props profile]
(let [vtoken (tokens/generate sprops (let [vtoken (tokens/generate props
{:iss :verify-email {:iss :verify-email
:exp (dt/in-future "72h") :exp (dt/in-future "72h")
:profile-id (:id profile) :profile-id (:id profile)
:email (:email profile)}) :email (:email profile)})
;; NOTE: this token is mainly used for possible complains ;; NOTE: this token is mainly used for possible complains
;; identification on the sns webhook ;; identification on the sns webhook
ptoken (tokens/generate sprops ptoken (tokens/generate props
{:iss :profile-identity {:iss :profile-identity
:profile-id (:id profile) :profile-id (:id profile)
:exp (dt/in-future {:days 30})})] :exp (dt/in-future {:days 30})})]
@ -360,8 +355,8 @@
:extra-data ptoken}))) :extra-data ptoken})))
(defn register-profile (defn register-profile
[{:keys [conn sprops session] :as cfg} {:keys [token] :as params}] [{:keys [conn session] :as cfg} {:keys [token] :as params}]
(let [claims (tokens/verify sprops {:token token :iss :prepared-register}) (let [claims (tokens/verify (::main/props cfg) {:token token :iss :prepared-register})
params (merge params claims) params (merge params claims)
is-active (or (:is-active params) is-active (or (:is-active params)
@ -377,7 +372,7 @@
(create-profile-relations conn) (create-profile-relations conn)
(profile/decode-profile-row))) (profile/decode-profile-row)))
invitation (when-let [token (:invitation-token params)] invitation (when-let [token (:invitation-token params)]
(tokens/verify sprops {:token token :iss :team-invitation}))] (tokens/verify (::main/props cfg) {:token token :iss :team-invitation}))]
;; If profile is filled in claims, means it tries to register ;; If profile is filled in claims, means it tries to register
;; again, so we proceed to update the modified-at attr ;; again, so we proceed to update the modified-at attr
@ -399,7 +394,7 @@
;; email. ;; email.
(and (some? invitation) (= (:email profile) (:member-email invitation))) (and (some? invitation) (= (:email profile) (:member-email invitation)))
(let [claims (assoc invitation :member-id (:id profile)) (let [claims (assoc invitation :member-id (:id profile))
token (tokens/generate sprops claims) token (tokens/generate (::main/props cfg) claims)
resp {:invitation-token token}] resp {:invitation-token token}]
(-> resp (-> resp
(rph/with-transform (session/create-fn session (:id profile))) (rph/with-transform (session/create-fn session (:id profile)))
@ -426,7 +421,7 @@
;; In all other cases, send a verification email. ;; In all other cases, send a verification email.
:else :else
(do (do
(send-email-verification! conn sprops profile) (send-email-verification! conn (::main/props cfg) profile)
(rph/with-meta profile (rph/with-meta profile
{::audit/replace-props (audit/profile->props profile) {::audit/replace-props (audit/profile->props profile)
::audit/profile-id (:id profile)}))))) ::audit/profile-id (:id profile)})))))
@ -435,10 +430,10 @@
(s/keys :req-un [::token ::fullname])) (s/keys :req-un [::token ::fullname]))
(sv/defmethod ::register-profile (sv/defmethod ::register-profile
{:auth false {::rpc/auth false
::climit/queue :auth ::climit/queue :auth
::doc/added "1.15"} ::doc/added "1.15"}
[{:keys [pool] :as cfg} params] [{:keys [::db/pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(-> (assoc cfg :conn conn) (-> (assoc cfg :conn conn)
(register-profile params)))) (register-profile params))))
@ -446,16 +441,16 @@
;; ---- COMMAND: Request Profile Recovery ;; ---- COMMAND: Request Profile Recovery
(defn request-profile-recovery (defn request-profile-recovery
[{:keys [pool sprops] :as cfg} {:keys [email] :as params}] [{:keys [::db/pool] :as cfg} {:keys [email] :as params}]
(letfn [(create-recovery-token [{:keys [id] :as profile}] (letfn [(create-recovery-token [{:keys [id] :as profile}]
(let [token (tokens/generate sprops (let [token (tokens/generate (::main/props cfg)
{:iss :password-recovery {:iss :password-recovery
:exp (dt/in-future "15m") :exp (dt/in-future "15m")
:profile-id id})] :profile-id id})]
(assoc profile :token token))) (assoc profile :token token)))
(send-email-notification [conn profile] (send-email-notification [conn profile]
(let [ptoken (tokens/generate sprops (let [ptoken (tokens/generate (::main/props cfg)
{:iss :profile-identity {:iss :profile-identity
:profile-id (:id profile) :profile-id (:id profile)
:exp (dt/in-future {:days 30})})] :exp (dt/in-future {:days 30})})]
@ -493,7 +488,7 @@
(s/keys :req-un [::email])) (s/keys :req-un [::email]))
(sv/defmethod ::request-profile-recovery (sv/defmethod ::request-profile-recovery
{:auth false {::rpc/auth false
::doc/added "1.15"} ::doc/added "1.15"}
[cfg params] [cfg params]
(request-profile-recovery cfg params)) (request-profile-recovery cfg params))

View file

@ -18,6 +18,7 @@
[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.media :as media] [app.media :as media]
[app.rpc :as-alias rpc]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
@ -866,18 +867,17 @@
;; --- Command: export-binfile ;; --- Command: export-binfile
(s/def ::file-id ::us/uuid) (s/def ::file-id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::include-libraries? ::us/boolean) (s/def ::include-libraries? ::us/boolean)
(s/def ::embed-assets? ::us/boolean) (s/def ::embed-assets? ::us/boolean)
(s/def ::export-binfile (s/def ::export-binfile
(s/keys :req-un [::profile-id ::file-id ::include-libraries? ::embed-assets?])) (s/keys :req [::rpc/profile-id] :req-un [::file-id ::include-libraries? ::embed-assets?]))
(sv/defmethod ::export-binfile (sv/defmethod ::export-binfile
"Export a penpot file in a binary format." "Export a penpot file in a binary format."
{::doc/added "1.15" {::doc/added "1.15"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [profile-id file-id include-libraries? embed-assets?] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id include-libraries? embed-assets?] :as params}]
(files/check-read-permissions! pool profile-id file-id) (files/check-read-permissions! pool profile-id file-id)
(let [body (reify yrs/StreamableResponseBody (let [body (reify yrs/StreamableResponseBody
(-write-body-to-stream [_ _ output-stream] (-write-body-to-stream [_ _ output-stream]
@ -892,13 +892,13 @@
(s/def ::file ::media/upload) (s/def ::file ::media/upload)
(s/def ::import-binfile (s/def ::import-binfile
(s/keys :req-un [::profile-id ::project-id ::file])) (s/keys :req [::rpc/profile-id] :req-un [::project-id ::file]))
(sv/defmethod ::import-binfile (sv/defmethod ::import-binfile
"Import a penpot file in a binary format." "Import a penpot file in a binary format."
{::doc/added "1.15" {::doc/added "1.15"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [profile-id project-id file] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id file] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(projects/check-read-permissions! conn profile-id project-id) (projects/check-read-permissions! conn profile-id project-id)
(let [ids (import! (assoc cfg (let [ids (import! (assoc cfg

View file

@ -12,6 +12,7 @@
[app.db :as db] [app.db :as db]
[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.rpc :as-alias rpc]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
@ -41,7 +42,7 @@
(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::share-id (s/nilable ::us/uuid))
(s/def ::get-comment-threads (s/def ::get-comment-threads
(s/and (s/keys :req-un [::profile-id] (s/and (s/keys :req [::rpc/profile-id]
:opt-un [::file-id ::share-id ::team-id]) :opt-un [::file-id ::share-id ::team-id])
#(or (:file-id %) (:team-id %)))) #(or (:file-id %) (:team-id %))))
@ -74,7 +75,7 @@
window w as (partition by c.thread_id order by c.created_at asc)") window w as (partition by c.thread_id order by c.created_at asc)")
(defn retrieve-comment-threads (defn retrieve-comment-threads
[conn {:keys [profile-id file-id share-id]}] [conn {:keys [::rpc/profile-id file-id share-id]}]
(files/check-comment-permissions! conn profile-id file-id share-id) (files/check-comment-permissions! conn profile-id file-id share-id)
(->> (db/exec! conn [sql:comment-threads profile-id file-id]) (->> (db/exec! conn [sql:comment-threads profile-id file-id])
(into [] (map decode-row)))) (into [] (map decode-row))))
@ -85,11 +86,12 @@
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::get-unread-comment-threads (s/def ::get-unread-comment-threads
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id]))
(sv/defmethod ::get-unread-comment-threads (sv/defmethod ::get-unread-comment-threads
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id team-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(teams/check-read-permissions! conn profile-id team-id) (teams/check-read-permissions! conn profile-id team-id)
(retrieve-unread-comment-threads conn params))) (retrieve-unread-comment-threads conn params)))
@ -122,7 +124,7 @@
"select * from threads where count_unread_comments > 0")) "select * from threads where count_unread_comments > 0"))
(defn retrieve-unread-comment-threads (defn retrieve-unread-comment-threads
[conn {:keys [profile-id team-id]}] [conn {:keys [::rpc/profile-id team-id]}]
(->> (db/exec! conn [sql:unread-comment-threads-by-team profile-id team-id]) (->> (db/exec! conn [sql:unread-comment-threads-by-team profile-id team-id])
(into [] (map decode-row)))) (into [] (map decode-row))))
@ -132,12 +134,13 @@
(s/def ::id ::us/uuid) (s/def ::id ::us/uuid)
(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::share-id (s/nilable ::us/uuid))
(s/def ::get-comment-thread (s/def ::get-comment-thread
(s/keys :req-un [::profile-id ::file-id ::id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::get-comment-thread (sv/defmethod ::get-comment-thread
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id id share-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(files/check-comment-permissions! conn profile-id file-id share-id) (files/check-comment-permissions! conn profile-id file-id share-id)
(let [sql (str "with threads as (" sql:comment-threads ")" (let [sql (str "with threads as (" sql:comment-threads ")"
@ -146,7 +149,7 @@
(decode-row))))) (decode-row)))))
(defn get-comment-thread (defn get-comment-thread
[conn {:keys [profile-id file-id id] :as params}] [conn {:keys [::rpc/profile-id file-id id] :as params}]
(let [sql (str "with threads as (" sql:comment-threads ")" (let [sql (str "with threads as (" sql:comment-threads ")"
"select * from threads where id = ?")] "select * from threads where id = ?")]
(-> (db/exec-one! conn [sql profile-id file-id id]) (-> (db/exec-one! conn [sql profile-id file-id id])
@ -160,12 +163,13 @@
(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::share-id (s/nilable ::us/uuid))
(s/def ::thread-id ::us/uuid) (s/def ::thread-id ::us/uuid)
(s/def ::get-comments (s/def ::get-comments
(s/keys :req-un [::profile-id ::thread-id] (s/keys :req [::rpc/profile-id]
:req-un [::thread-id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::get-comments (sv/defmethod ::get-comments
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id thread-id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id thread-id share-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(let [thread (db/get-by-id conn :comment-thread thread-id)] (let [thread (db/get-by-id conn :comment-thread thread-id)]
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id)) (files/check-comment-permissions! conn profile-id (:file-id thread) share-id))
@ -191,7 +195,8 @@
(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::share-id (s/nilable ::us/uuid))
(s/def ::get-profiles-for-file-comments (s/def ::get-profiles-for-file-comments
(s/keys :req-un [::profile-id ::file-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::get-profiles-for-file-comments (sv/defmethod ::get-profiles-for-file-comments
@ -199,7 +204,7 @@
participants on comment threads of the file." participants on comment threads of the file."
{::doc/added "1.15" {::doc/added "1.15"
::doc/changes ["1.15" "Imported from queries and renamed."]} ::doc/changes ["1.15" "Imported from queries and renamed."]}
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id]}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id share-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(files/check-comment-permissions! conn profile-id file-id share-id) (files/check-comment-permissions! conn profile-id file-id share-id)
(get-file-comments-users conn file-id profile-id))) (get-file-comments-users conn file-id profile-id)))
@ -240,19 +245,19 @@
(s/def ::page-id ::us/uuid) (s/def ::page-id ::us/uuid)
(s/def ::file-id ::us/uuid) (s/def ::file-id ::us/uuid)
(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::share-id (s/nilable ::us/uuid))
(s/def ::profile-id ::us/uuid)
(s/def ::position ::gpt/point) (s/def ::position ::gpt/point)
(s/def ::content ::us/string) (s/def ::content ::us/string)
(s/def ::frame-id ::us/uuid) (s/def ::frame-id ::us/uuid)
(s/def ::create-comment-thread (s/def ::create-comment-thread
(s/keys :req-un [::profile-id ::file-id ::position ::content ::page-id ::frame-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::position ::content ::page-id ::frame-id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::create-comment-thread (sv/defmethod ::create-comment-thread
{::doc/added "1.15" {::doc/added "1.15"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id share-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(files/check-comment-permissions! conn profile-id file-id share-id) (files/check-comment-permissions! conn profile-id file-id share-id)
@ -268,7 +273,7 @@
(:next-seqn res))) (:next-seqn res)))
(defn create-comment-thread (defn create-comment-thread
[conn {:keys [profile-id file-id page-id position content frame-id] :as params}] [conn {:keys [::rpc/profile-id file-id page-id position content frame-id] :as params}]
(let [seqn (retrieve-next-seqn conn file-id) (let [seqn (retrieve-next-seqn conn file-id)
now (dt/now) now (dt/now)
pname (retrieve-page-name conn params) pname (retrieve-page-name conn params)
@ -316,12 +321,13 @@
(s/def ::share-id (s/nilable ::us/uuid)) (s/def ::share-id (s/nilable ::us/uuid))
(s/def ::update-comment-thread-status (s/def ::update-comment-thread-status
(s/keys :req-un [::profile-id ::id] (s/keys :req [::rpc/profile-id]
:req-un [::id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::update-comment-thread-status (sv/defmethod ::update-comment-thread-status
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id share-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [cthr (db/get-by-id conn :comment-thread id {:for-update true})] (let [cthr (db/get-by-id conn :comment-thread id {:for-update true})]
(when-not cthr (when-not cthr
@ -346,12 +352,13 @@
(s/def ::is-resolved ::us/boolean) (s/def ::is-resolved ::us/boolean)
(s/def ::update-comment-thread (s/def ::update-comment-thread
(s/keys :req-un [::profile-id ::id ::is-resolved] (s/keys :req [::rpc/profile-id]
:req-un [::id ::is-resolved]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::update-comment-thread (sv/defmethod ::update-comment-thread
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id id is-resolved share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id is-resolved share-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})] (let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
(when-not thread (when-not thread
@ -370,7 +377,8 @@
(declare create-comment) (declare create-comment)
(s/def ::create-comment (s/def ::create-comment
(s/keys :req-un [::profile-id ::thread-id ::content] (s/keys :req [::rpc/profile-id]
:req-un [::thread-id ::content]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::create-comment (sv/defmethod ::create-comment
@ -381,7 +389,7 @@
(create-comment conn params))) (create-comment conn params)))
(defn create-comment (defn create-comment
[conn {:keys [profile-id thread-id content share-id] :as params}] [conn {:keys [::rpc/profile-id thread-id content share-id] :as params}]
(let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true}) (let [thread (-> (db/get-by-id conn :comment-thread thread-id {:for-update true})
(decode-row)) (decode-row))
pname (retrieve-page-name conn thread)] pname (retrieve-page-name conn thread)]
@ -437,7 +445,8 @@
(declare update-comment) (declare update-comment)
(s/def ::update-comment (s/def ::update-comment
(s/keys :req-un [::profile-id ::id ::content] (s/keys :req [::rpc/profile-id]
:req-un [::id ::content]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::update-comment (sv/defmethod ::update-comment
@ -447,7 +456,7 @@
(update-comment conn params))) (update-comment conn params)))
(defn update-comment (defn update-comment
[conn {:keys [profile-id id content share-id] :as params}] [conn {:keys [::rpc/profile-id id content share-id] :as params}]
(let [comment (db/get-by-id conn :comment id {:for-update true}) (let [comment (db/get-by-id conn :comment id {:for-update true})
_ (when-not comment (ex/raise :type :not-found)) _ (when-not comment (ex/raise :type :not-found))
thread (db/get-by-id conn :comment-thread (:thread-id comment) {:for-update true}) thread (db/get-by-id conn :comment-thread (:thread-id comment) {:for-update true})
@ -476,11 +485,12 @@
;; --- COMMAND: Delete Comment Thread ;; --- COMMAND: Delete Comment Thread
(s/def ::delete-comment-thread (s/def ::delete-comment-thread
(s/keys :req-un [::profile-id ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::id]))
(sv/defmethod ::delete-comment-thread (sv/defmethod ::delete-comment-thread
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})] (let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
(when-not (= (:owner-id thread) profile-id) (when-not (= (:owner-id thread) profile-id)
@ -493,11 +503,12 @@
;; --- COMMAND: Delete comment ;; --- COMMAND: Delete comment
(s/def ::delete-comment (s/def ::delete-comment
(s/keys :req-un [::profile-id ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::id]))
(sv/defmethod ::delete-comment (sv/defmethod ::delete-comment
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [comment (db/get-by-id conn :comment id {:for-update true})] (let [comment (db/get-by-id conn :comment id {:for-update true})]
(when-not (= (:owner-id comment) profile-id) (when-not (= (:owner-id comment) profile-id)
@ -509,12 +520,13 @@
;; --- COMMAND: Update comment thread position ;; --- COMMAND: Update comment thread position
(s/def ::update-comment-thread-position (s/def ::update-comment-thread-position
(s/keys :req-un [::profile-id ::id ::position ::frame-id] (s/keys :req [::rpc/profile-id]
:req-un [::id ::position ::frame-id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::update-comment-thread-position (sv/defmethod ::update-comment-thread-position
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id id position frame-id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id position frame-id share-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})] (let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id) (files/check-comment-permissions! conn profile-id (:file-id thread) share-id)
@ -528,12 +540,13 @@
;; --- COMMAND: Update comment frame ;; --- COMMAND: Update comment frame
(s/def ::update-comment-thread-frame (s/def ::update-comment-thread-frame
(s/keys :req-un [::profile-id ::id ::frame-id] (s/keys :req [::rpc/profile-id]
:req-un [::id ::frame-id]
:opt-un [::share-id])) :opt-un [::share-id]))
(sv/defmethod ::update-comment-thread-frame (sv/defmethod ::update-comment-thread-frame
{::doc/added "1.15"} {::doc/added "1.15"}
[{:keys [pool] :as cfg} {:keys [profile-id id frame-id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id frame-id share-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [thread (db/get-by-id conn :comment-thread id {:for-update true})] (let [thread (db/get-by-id conn :comment-thread id {:for-update true})]
(files/check-comment-permissions! conn profile-id (:file-id thread) share-id) (files/check-comment-permissions! conn profile-id (:file-id thread) share-id)

View file

@ -12,6 +12,7 @@
[app.config :as cf] [app.config :as cf]
[app.db :as db] [app.db :as db]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.rpc :as-alias rpc]
[app.rpc.commands.auth :as cmd.auth] [app.rpc.commands.auth :as cmd.auth]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.util.services :as sv] [app.util.services :as sv]
@ -26,7 +27,7 @@
"A command that is responsible of creating a demo purpose "A command that is responsible of creating a demo purpose
profile. It only works if the `demo-users` flag is enabled in the profile. It only works if the `demo-users` flag is enabled in the
configuration." configuration."
{:auth false {::rpc/auth false
::doc/added "1.15" ::doc/added "1.15"
::doc/changes ["1.15" "This method is migrated from mutations to commands."]} ::doc/changes ["1.15" "This method is migrated from mutations to commands."]}
[{:keys [pool] :as cfg} _] [{:keys [pool] :as cfg} _]

View file

@ -19,6 +19,7 @@
[app.db.sql :as sql] [app.db.sql :as sql]
[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.rpc :as-alias rpc]
[app.rpc.commands.files.thumbnails :as-alias thumbs] [app.rpc.commands.files.thumbnails :as-alias thumbs]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.rpc.cond :as-alias cond] [app.rpc.cond :as-alias cond]
@ -52,7 +53,6 @@
(s/def ::id ::us/uuid) (s/def ::id ::us/uuid)
(s/def ::is-shared ::us/boolean) (s/def ::is-shared ::us/boolean)
(s/def ::name ::us/string) (s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::project-id ::us/uuid) (s/def ::project-id ::us/uuid)
(s/def ::search-term ::us/string) (s/def ::search-term ::us/string)
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
@ -257,7 +257,8 @@
(str (dt/format-instant modified-at :iso) "-" revn)) (str (dt/format-instant modified-at :iso) "-" revn))
(s/def ::get-file (s/def ::get-file
(s/keys :req-un [::profile-id ::id] (s/keys :req [::rpc/profile-id]
:req-un [::id]
:opt-un [::features])) :opt-un [::features]))
(sv/defmethod ::get-file (sv/defmethod ::get-file
@ -265,7 +266,7 @@
{::doc/added "1.17" {::doc/added "1.17"
::cond/get-object #(get-minimal-file %1 (:id %2)) ::cond/get-object #(get-minimal-file %1 (:id %2))
::cond/key-fn get-file-etag} ::cond/key-fn get-file-etag}
[{:keys [pool] :as cfg} {:keys [profile-id id features] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id features] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(let [perms (get-permissions conn profile-id id)] (let [perms (get-permissions conn profile-id id)]
(check-read-permissions! perms) (check-read-permissions! perms)
@ -286,13 +287,14 @@
(s/def ::get-file-fragment (s/def ::get-file-fragment
(s/keys :req-un [::file-id ::fragment-id] (s/keys :req-un [::file-id ::fragment-id]
:opt-un [::share-id ::profile-id])) :opt [::rpc/profile-id]
:opt-un [::share-id]))
(sv/defmethod ::get-file-fragment (sv/defmethod ::get-file-fragment
"Retrieve a file by its ID. Only authenticated users." "Retrieve a file by its ID. Only authenticated users."
{::doc/added "1.17" {::doc/added "1.17"
:auth false} ::rpc/:auth false}
[{:keys [pool] :as cfg} {:keys [profile-id file-id fragment-id share-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id fragment-id share-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(let [perms (get-permissions conn profile-id file-id share-id)] (let [perms (get-permissions conn profile-id file-id share-id)]
(check-read-permissions! perms) (check-read-permissions! perms)
@ -320,7 +322,7 @@
(d/index-by :object-id :data))))) (d/index-by :object-id :data)))))
(s/def ::get-file-object-thumbnails (s/def ::get-file-object-thumbnails
(s/keys :req-un [::profile-id ::file-id])) (s/keys :req [::rpc/profile-id] :req-un [::file-id]))
(sv/defmethod ::get-file-object-thumbnails (sv/defmethod ::get-file-object-thumbnails
"Retrieve a file object thumbnails." "Retrieve a file object thumbnails."
@ -328,7 +330,7 @@
::cond/get-object #(get-minimal-file %1 (:file-id %2)) ::cond/get-object #(get-minimal-file %1 (:file-id %2))
::cond/reuse-key? true ::cond/reuse-key? true
::cond/key-fn get-file-etag} ::cond/key-fn get-file-etag}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id) (check-read-permissions! conn profile-id file-id)
(get-object-thumbnails conn file-id))) (get-object-thumbnails conn file-id)))
@ -350,7 +352,7 @@
order by f.modified_at desc") order by f.modified_at desc")
(s/def ::get-project-files (s/def ::get-project-files
(s/keys :req-un [::profile-id ::project-id])) (s/keys :req [::rpc/profile-id] :req-un [::project-id]))
(defn get-project-files (defn get-project-files
[conn project-id] [conn project-id]
@ -359,7 +361,7 @@
(sv/defmethod ::get-project-files (sv/defmethod ::get-project-files
"Get all files for the specified project." "Get all files for the specified project."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(projects/check-read-permissions! conn profile-id project-id) (projects/check-read-permissions! conn profile-id project-id)
(get-project-files conn project-id))) (get-project-files conn project-id)))
@ -370,15 +372,14 @@
(declare get-has-file-libraries) (declare get-has-file-libraries)
(s/def ::file-id ::us/uuid) (s/def ::file-id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::has-file-libraries (s/def ::has-file-libraries
(s/keys :req-un [::profile-id ::file-id])) (s/keys :req [::rpc/profile-id] :req-un [::file-id]))
(sv/defmethod ::has-file-libraries (sv/defmethod ::has-file-libraries
"Checks if the file has libraries. Returns a boolean" "Checks if the file has libraries. Returns a boolean"
{::doc/added "1.15.1"} {::doc/added "1.15.1"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! pool profile-id file-id) (check-read-permissions! pool profile-id file-id)
(get-has-file-libraries conn params))) (get-has-file-libraries conn params)))
@ -426,7 +427,8 @@
(s/def ::object-id ::us/uuid) (s/def ::object-id ::us/uuid)
(s/def ::get-page (s/def ::get-page
(s/and (s/and
(s/keys :req-un [::profile-id ::file-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id]
:opt-un [::page-id ::object-id ::features]) :opt-un [::page-id ::object-id ::features])
(fn [obj] (fn [obj]
(if (contains? obj :object-id) (if (contains? obj :object-id)
@ -444,7 +446,7 @@
Mainly used for rendering purposes." Mainly used for rendering purposes."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id) (check-read-permissions! conn profile-id file-id)
(get-page conn params))) (get-page conn params)))
@ -493,7 +495,7 @@
(into #{} xform (db/exec! conn [sql:team-shared-files team-id])))) (into #{} xform (db/exec! conn [sql:team-shared-files team-id]))))
(s/def ::get-team-shared-files (s/def ::get-team-shared-files
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id] :req-un [::team-id]))
(sv/defmethod ::get-team-shared-files (sv/defmethod ::get-team-shared-files
"Get all file (libraries) for the specified team." "Get all file (libraries) for the specified team."
@ -542,13 +544,14 @@
(handle-file-features client-features))))))) (handle-file-features client-features)))))))
(s/def ::get-file-libraries (s/def ::get-file-libraries
(s/keys :req-un [::profile-id ::file-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id]
:opt-un [::features])) :opt-un [::features]))
(sv/defmethod ::get-file-libraries (sv/defmethod ::get-file-libraries
"Get libraries used by the specified file." "Get libraries used by the specified file."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id features] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id features] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id) (check-read-permissions! conn profile-id file-id)
(get-file-libraries conn file-id features))) (get-file-libraries conn file-id features)))
@ -569,12 +572,12 @@
(db/exec! conn [sql:library-using-files file-id])) (db/exec! conn [sql:library-using-files file-id]))
(s/def ::get-library-file-references (s/def ::get-library-file-references
(s/keys :req-un [::profile-id ::file-id])) (s/keys :req [::rpc/profile-id] :req-un [::file-id]))
(sv/defmethod ::get-library-file-references (sv/defmethod ::get-library-file-references
"Returns all the file references that use specified file (library) id." "Returns all the file references that use specified file (library) id."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id) (check-read-permissions! conn profile-id file-id)
(get-library-file-references conn file-id))) (get-library-file-references conn file-id)))
@ -607,11 +610,12 @@
(db/exec! conn [sql:team-recent-files team-id])) (db/exec! conn [sql:team-recent-files team-id]))
(s/def ::get-team-recent-files (s/def ::get-team-recent-files
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id]))
(sv/defmethod ::get-team-recent-files (sv/defmethod ::get-team-recent-files
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id team-id]}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(teams/check-read-permissions! conn profile-id team-id) (teams/check-read-permissions! conn profile-id team-id)
(get-team-recent-files conn team-id))) (get-team-recent-files conn team-id)))
@ -639,12 +643,13 @@
(s/def ::revn ::us/integer) (s/def ::revn ::us/integer)
(s/def ::get-file-thumbnail (s/def ::get-file-thumbnail
(s/keys :req-un [::profile-id ::file-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id]
:opt-un [::revn])) :opt-un [::revn]))
(sv/defmethod ::get-file-thumbnail (sv/defmethod ::get-file-thumbnail
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool]} {:keys [profile-id file-id revn]}] [{:keys [pool]} {:keys [::rpc/profile-id file-id revn]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id) (check-read-permissions! conn profile-id file-id)
(-> (get-file-thumbnail conn file-id revn) (-> (get-file-thumbnail conn file-id revn)
@ -730,14 +735,15 @@
(update :objects assoc-thumbnails page-id thumbs))))) (update :objects assoc-thumbnails page-id thumbs)))))
(s/def ::get-file-data-for-thumbnail (s/def ::get-file-data-for-thumbnail
(s/keys :req-un [::profile-id ::file-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id]
:opt-un [::features])) :opt-un [::features]))
(sv/defmethod ::get-file-data-for-thumbnail (sv/defmethod ::get-file-data-for-thumbnail
"Retrieves the data for generate the thumbnail of the file. Used "Retrieves the data for generate the thumbnail of the file. Used
mainly for render thumbnails on dashboard." mainly for render thumbnails on dashboard."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id features] :as props}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id features] :as props}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id) (check-read-permissions! conn profile-id file-id)
(let [file (get-file conn file-id features)] (let [file (get-file conn file-id features)]
@ -760,12 +766,13 @@
{:id id})) {:id id}))
(s/def ::rename-file (s/def ::rename-file
(s/keys :req-un [::profile-id ::name ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::name ::id]))
(sv/defmethod ::rename-file (sv/defmethod ::rename-file
{::doc/added "1.17" {::doc/added "1.17"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id id) (check-edition-permissions! conn profile-id id)
(let [file (rename-file conn params)] (let [file (rename-file conn params)]
@ -808,12 +815,13 @@
{:id id}))))))))) {:id id})))))))))
(s/def ::set-file-shared (s/def ::set-file-shared
(s/keys :req-un [::profile-id ::id ::is-shared])) (s/keys :req [::rpc/profile-id]
:req-un [::id ::is-shared]))
(sv/defmethod ::set-file-shared (sv/defmethod ::set-file-shared
{::doc/added "1.17" {::doc/added "1.17"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [id profile-id is-shared] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id is-shared] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id id) (check-edition-permissions! conn profile-id id)
(when-not is-shared (when-not is-shared
@ -836,16 +844,18 @@
{:id id})) {:id id}))
(s/def ::delete-file (s/def ::delete-file
(s/keys :req-un [::id ::profile-id])) (s/keys :req [::rpc/profile-id]
:req-un [::id]))
(sv/defmethod ::delete-file (sv/defmethod ::delete-file
{::doc/added "1.17" {::doc/added "1.17"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id id) (check-edition-permissions! conn profile-id id)
(absorb-library conn params) (absorb-library conn params)
(let [file (mark-file-deleted conn params)] (let [file (mark-file-deleted conn params)]
(rph/with-meta (rph/wrap) (rph/with-meta (rph/wrap)
{::audit/props {:project-id (:project-id file) {::audit/props {:project-id (:project-id file)
:name (:name file) :name (:name file)
@ -864,12 +874,13 @@
(db/exec-one! conn [sql:link-file-to-library file-id library-id])) (db/exec-one! conn [sql:link-file-to-library file-id library-id]))
(s/def ::link-file-to-library (s/def ::link-file-to-library
(s/keys :req-un [::profile-id ::file-id ::library-id])) (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::library-id]))
(sv/defmethod ::link-file-to-library (sv/defmethod ::link-file-to-library
{::doc/added "1.17" {::doc/added "1.17"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [profile-id file-id library-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id library-id] :as params}]
(when (= file-id library-id) (when (= file-id library-id)
(ex/raise :type :validation (ex/raise :type :validation
:code :invalid-library :code :invalid-library
@ -888,12 +899,13 @@
:library-file-id library-id})) :library-file-id library-id}))
(s/def ::unlink-file-from-library (s/def ::unlink-file-from-library
(s/keys :req-un [::profile-id ::file-id ::library-id])) (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::library-id]))
(sv/defmethod ::unlink-file-from-library (sv/defmethod ::unlink-file-from-library
{::doc/added "1.17" {::doc/added "1.17"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id file-id) (check-edition-permissions! conn profile-id file-id)
(unlink-file-from-library conn params))) (unlink-file-from-library conn params)))
@ -909,14 +921,15 @@
:library-file-id library-id})) :library-file-id library-id}))
(s/def ::update-file-library-sync-status (s/def ::update-file-library-sync-status
(s/keys :req-un [::profile-id ::file-id ::library-id])) (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::library-id]))
;; TODO: improve naming ;; TODO: improve naming
(sv/defmethod ::update-file-library-sync-status (sv/defmethod ::update-file-library-sync-status
"Update the synchronization statos of a file->library link" "Update the synchronization statos of a file->library link"
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id file-id) (check-edition-permissions! conn profile-id file-id)
(update-sync conn params))) (update-sync conn params)))
@ -931,13 +944,14 @@
{:id file-id})) {:id file-id}))
(s/def ::ignore-file-library-sync-status (s/def ::ignore-file-library-sync-status
(s/keys :req-un [::profile-id ::file-id ::date])) (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::date]))
;; TODO: improve naming ;; TODO: improve naming
(sv/defmethod ::ignore-file-library-sync-status (sv/defmethod ::ignore-file-library-sync-status
"Ignore updates in linked files" "Ignore updates in linked files"
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id file-id) (check-edition-permissions! conn profile-id file-id)
(ignore-sync conn params))) (ignore-sync conn params)))
@ -960,11 +974,13 @@
(s/def ::data (s/nilable ::us/string)) (s/def ::data (s/nilable ::us/string))
(s/def ::thumbs/object-id ::us/string) (s/def ::thumbs/object-id ::us/string)
(s/def ::upsert-file-object-thumbnail (s/def ::upsert-file-object-thumbnail
(s/keys :req-un [::profile-id ::file-id ::thumbs/object-id ::data])) (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::thumbs/object-id]
:opt-un [::data]))
(sv/defmethod ::upsert-file-object-thumbnail (sv/defmethod ::upsert-file-object-thumbnail
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id file-id) (check-edition-permissions! conn profile-id file-id)
(upsert-file-object-thumbnail! conn params) (upsert-file-object-thumbnail! conn params)
@ -987,13 +1003,14 @@
(s/def ::revn ::us/integer) (s/def ::revn ::us/integer)
(s/def ::props map?) (s/def ::props map?)
(s/def ::upsert-file-thumbnail (s/def ::upsert-file-thumbnail
(s/keys :req-un [::profile-id ::file-id ::revn ::data ::props])) (s/keys :req [::rpc/profile-id]
:req-un [::file-id ::revn ::data ::props]))
(sv/defmethod ::upsert-file-thumbnail (sv/defmethod ::upsert-file-thumbnail
"Creates or updates the file thumbnail. Mainly used for paint the "Creates or updates the file thumbnail. Mainly used for paint the
grid thumbnails." grid thumbnails."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id file-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id file-id) (check-edition-permissions! conn profile-id file-id)
(upsert-file-thumbnail conn params) (upsert-file-thumbnail conn params)

View file

@ -13,6 +13,7 @@
[app.db :as db] [app.db :as db]
[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.rpc :as-alias rpc]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.permissions :as perms] [app.rpc.permissions :as perms]
@ -68,8 +69,8 @@
(files/decode-row file))) (files/decode-row file)))
(s/def ::create-file (s/def ::create-file
(s/keys :req-un [::files/profile-id (s/keys :req [::rpc/profile-id]
::files/name :req-un [::files/name
::files/project-id] ::files/project-id]
:opt-un [::files/id :opt-un [::files/id
::files/is-shared ::files/is-shared
@ -78,10 +79,11 @@
(sv/defmethod ::create-file (sv/defmethod ::create-file
{::doc/added "1.17" {::doc/added "1.17"
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(proj/check-edition-permissions! conn profile-id project-id) (proj/check-edition-permissions! conn profile-id project-id)
(let [team-id (files/get-team-id conn project-id)] (let [team-id (files/get-team-id conn project-id)
params (assoc params :profile-id profile-id)]
(-> (create-file conn params) (-> (create-file conn params)
(vary-meta assoc ::audit/props {:team-id team-id}))))) (vary-meta assoc ::audit/props {:team-id team-id})))))

View file

@ -11,6 +11,7 @@
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.commands.files.create :as files.create] [app.rpc.commands.files.create :as files.create]
[app.rpc.commands.files.update :as files.update] [app.rpc.commands.files.update :as files.update]
@ -26,8 +27,8 @@
(s/def ::create-page ::us/boolean) (s/def ::create-page ::us/boolean)
(s/def ::create-temp-file (s/def ::create-temp-file
(s/keys :req-un [::files/profile-id (s/keys :req [::rpc/profile-id]
::files/name :req-un [::files/name
::files/project-id] ::files/project-id]
:opt-un [::files/id :opt-un [::files/id
::files/is-shared ::files/is-shared
@ -36,7 +37,7 @@
(sv/defmethod ::create-temp-file (sv/defmethod ::create-temp-file
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id project-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(proj/check-edition-permissions! conn profile-id project-id) (proj/check-edition-permissions! conn profile-id project-id)
(files.create/create-file conn (assoc params :deleted-at (dt/in-future {:days 1}))))) (files.create/create-file conn (assoc params :deleted-at (dt/in-future {:days 1})))))
@ -44,7 +45,7 @@
;; --- MUTATION COMMAND: update-temp-file ;; --- MUTATION COMMAND: update-temp-file
(defn update-temp-file (defn update-temp-file
[conn {:keys [profile-id session-id id revn changes] :as params}] [conn {:keys [::rpc/profile-id session-id id revn changes] :as params}]
(db/insert! conn :file-change (db/insert! conn :file-change
{:id (uuid/next) {:id (uuid/next)
:session-id session-id :session-id session-id
@ -95,12 +96,12 @@
nil)) nil))
(s/def ::persist-temp-file (s/def ::persist-temp-file
(s/keys :req-un [::files/id (s/keys :req [::rpc/profile-id]
::files/profile-id])) :req-un [::files/id]))
(sv/defmethod ::persist-temp-file (sv/defmethod ::persist-temp-file
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(files/check-edition-permissions! conn profile-id id) (files/check-edition-permissions! conn profile-id id)
(persist-temp-file conn params))) (persist-temp-file conn params)))

View file

@ -20,6 +20,7 @@
[app.loggers.webhooks :as-alias webhooks] [app.loggers.webhooks :as-alias webhooks]
[app.metrics :as mtx] [app.metrics :as mtx]
[app.msgbus :as mbus] [app.msgbus :as mbus]
[app.rpc :as-alias rpc]
[app.rpc.climit :as-alias climit] [app.rpc.climit :as-alias climit]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
@ -52,7 +53,8 @@
(s/def ::revn ::us/integer) (s/def ::revn ::us/integer)
(s/def ::update-file (s/def ::update-file
(s/and (s/and
(s/keys :req-un [::files/id ::files/profile-id ::session-id ::revn] (s/keys :req [::rpc/profile-id]
:req-un [::files/id ::session-id ::revn]
:opt-un [::changes ::changes-with-metadata ::features]) :opt-un [::changes ::changes-with-metadata ::features])
(fn [o] (fn [o]
(or (contains? o :changes) (or (contains? o :changes)
@ -130,19 +132,20 @@
::webhooks/batch-timeout (dt/duration "2m") ::webhooks/batch-timeout (dt/duration "2m")
::webhooks/batch-key :id ::webhooks/batch-key :id
::doc/added "1.17"} ::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(files/check-edition-permissions! conn profile-id id) (files/check-edition-permissions! conn profile-id id)
(db/xact-lock! conn id) (db/xact-lock! conn id)
(let [cfg (assoc cfg :conn conn) (let [cfg (assoc cfg :conn conn)
params (assoc params :profile-id profile-id)
tpoint (dt/tpoint)] tpoint (dt/tpoint)]
(-> (update-file cfg params) (-> (update-file cfg params)
(rph/with-defer #(let [elapsed (tpoint)] (rph/with-defer #(let [elapsed (tpoint)]
(l/trace :hint "update-file" :time (dt/format-duration elapsed)))))))) (l/trace :hint "update-file" :time (dt/format-duration elapsed))))))))
(defn update-file (defn update-file
[{:keys [conn metrics] :as cfg} {:keys [id profile-id changes changes-with-metadata] :as params}] [{:keys [conn metrics] :as cfg} {:keys [profile-id id changes changes-with-metadata] :as params}]
(let [file (get-file conn id) (let [file (get-file conn id)
features (->> (concat (:features file) features (->> (concat (:features file)
(:features params)) (:features params))
@ -184,7 +187,7 @@
:team-id (:team-id file)})))))) :team-id (:team-id file)}))))))
(defn- update-file* (defn- update-file*
[{:keys [conn] :as cfg} {:keys [file changes session-id profile-id] :as params}] [{:keys [conn] :as cfg} {:keys [profile-id file changes session-id] :as params}]
(when (> (:revn params) (when (> (:revn params)
(:revn file)) (:revn file))
(ex/raise :type :validation (ex/raise :type :validation

View file

@ -14,6 +14,7 @@
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.loggers.webhooks :as-alias webhooks] [app.loggers.webhooks :as-alias webhooks]
[app.rpc :as-alias rpc]
[app.rpc.commands.binfile :as binfile] [app.rpc.commands.binfile :as binfile]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.commands.teams :as teams :refer [create-project-role create-project]] [app.rpc.commands.teams :as teams :refer [create-project-role create-project]]
@ -31,14 +32,14 @@
(declare duplicate-file) (declare duplicate-file)
(s/def ::id ::us/uuid) (s/def ::id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::project-id ::us/uuid) (s/def ::project-id ::us/uuid)
(s/def ::file-id ::us/uuid) (s/def ::file-id ::us/uuid)
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::name ::us/string) (s/def ::name ::us/string)
(s/def ::duplicate-file (s/def ::duplicate-file
(s/keys :req-un [::profile-id ::file-id] (s/keys :req [::rpc/profile-id]
:req-un [::file-id]
:opt-un [::name])) :opt-un [::name]))
(sv/defmethod ::duplicate-file (sv/defmethod ::duplicate-file
@ -47,7 +48,7 @@
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} params] [{:keys [pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(duplicate-file conn params))) (duplicate-file conn (assoc params :profile-id (::rpc/profile-id params)))))
(defn- remap-id (defn- remap-id
[item index key] [item index key]
@ -212,7 +213,8 @@
(declare duplicate-project) (declare duplicate-project)
(s/def ::duplicate-project (s/def ::duplicate-project
(s/keys :req-un [::profile-id ::project-id] (s/keys :req [::rpc/profile-id]
:req-un [::project-id]
:opt-un [::name])) :opt-un [::name]))
(sv/defmethod ::duplicate-project (sv/defmethod ::duplicate-project
@ -221,7 +223,7 @@
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} params] [{:keys [pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(duplicate-project conn params))) (duplicate-project conn (assoc params :profile-id (::rpc/profile-id params)))))
(defn duplicate-project (defn duplicate-project
[conn {:keys [profile-id project-id name] :as params}] [conn {:keys [profile-id project-id name] :as params}]
@ -249,9 +251,7 @@
;; create the duplicated project and assign the current profile as ;; create the duplicated project and assign the current profile as
;; a project owner ;; a project owner
(create-project conn project) (create-project conn project)
(create-project-role conn {:project-id (:id project) (create-project-role conn profile-id (:id project) :owner)
:profile-id profile-id
:role :owner})
;; duplicate all files ;; duplicate all files
(let [index (reduce #(assoc %1 (:id %2) (uuid/next)) {} files) (let [index (reduce #(assoc %1 (:id %2) (uuid/next)) {} files)
@ -322,7 +322,8 @@
(s/def ::ids (s/every ::us/uuid :kind set?)) (s/def ::ids (s/every ::us/uuid :kind set?))
(s/def ::move-files (s/def ::move-files
(s/keys :req-un [::profile-id ::ids ::project-id])) (s/keys :req [::rpc/profile-id]
:req-un [::ids ::project-id]))
(sv/defmethod ::move-files (sv/defmethod ::move-files
"Move a set of files from one project to other." "Move a set of files from one project to other."
@ -330,7 +331,7 @@
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} params] [{:keys [pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(move-files conn params))) (move-files conn (assoc params :profile-id (::rpc/profile-id params)))))
;; --- COMMAND: Move project ;; --- COMMAND: Move project
@ -362,7 +363,8 @@
(s/def ::move-project (s/def ::move-project
(s/keys :req-un [::profile-id ::team-id ::project-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::project-id]))
(sv/defmethod ::move-project (sv/defmethod ::move-project
"Move projects between teams." "Move projects between teams."
@ -370,7 +372,7 @@
::webhooks/event? true} ::webhooks/event? true}
[{:keys [pool] :as cfg} params] [{:keys [pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(move-project conn params))) (move-project conn (assoc params :profile-id (::rpc/profile-id params)))))
;; --- COMMAND: Clone Template ;; --- COMMAND: Clone Template
@ -378,7 +380,8 @@
(s/def ::template-id ::us/not-empty-string) (s/def ::template-id ::us/not-empty-string)
(s/def ::clone-template (s/def ::clone-template
(s/keys :req-un [::profile-id ::project-id ::template-id])) (s/keys :req [::rpc/profile-id]
:req-un [::project-id ::template-id]))
(sv/defmethod ::clone-template (sv/defmethod ::clone-template
"Clone into the specified project the template by its id." "Clone into the specified project the template by its id."
@ -387,7 +390,7 @@
[{:keys [pool] :as cfg} params] [{:keys [pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(-> (assoc cfg :conn conn) (-> (assoc cfg :conn conn)
(clone-template params)))) (clone-template (assoc params :profile-id (::rpc/profile-id params))))))
(defn- clone-template (defn- clone-template
[{:keys [conn templates] :as cfg} {:keys [profile-id template-id project-id]}] [{:keys [conn templates] :as cfg} {:keys [profile-id template-id project-id]}]

View file

@ -0,0 +1,75 @@
;; 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
(ns app.rpc.commands.profile
(:require
[app.auth :as auth]
[app.common.exceptions :as ex]
[app.common.spec :as us]
[app.config :as cf]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.climit :as-alias climit]
[app.rpc.doc :as-alias doc]
[app.util.services :as sv]
[clojure.spec.alpha :as s]))
;; --- MUTATION: Set profile password
(declare update-profile-password!)
(s/def ::profile-id ::us/uuid)
(s/def ::password ::us/not-empty-string)
(s/def ::get-derived-password
(s/keys :req [::rpc/profile-id]
:req-un [::password]))
(sv/defmethod ::get-derived-password
"Get derived password, only ADMINS allowed to call this RPC
methods. Designed for administration pannel integration."
{::climit/queue :auth
::climit/key-fn ::rpc/profile-id
::doc/added "1.18"}
[{:keys [::db/pool]} {:keys [password] :as params}]
(db/with-atomic [conn pool]
(let [admins (cf/get :admins)
profile (db/get-by-id conn :profile (::rpc/profile-id params))]
(if (or (:is-admin profile)
(contains? admins (:email profile)))
{:password (auth/derive-password password)}
(ex/raise :type :authentication
:code :only-admins-allowed
:hint "only admins allowed to call this RPC method")))))
;; --- MUTATION: Check profile password
(s/def ::attempt ::us/not-empty-string)
(s/def ::check-profile-password
(s/keys :req [::rpc/profile-id]
:req-un [::profile-id ::password]))
(sv/defmethod ::check-profile-password
"Check profile password, only ADMINS allowed to call this RPC
methods. Designed for administration pannel integration."
{::climit/queue :auth
::climit/key-fn ::rpc/profile-id
::doc/added "1.18"}
[{:keys [::db/pool]} {:keys [profile-id password] :as params}]
(db/with-atomic [conn pool]
(let [admins (cf/get :admins)
profile (db/get-by-id pool :profile (::rpc/profile-id params))]
(if (or (:is-admin profile)
(contains? admins (:email profile)))
(let [profile (if (not= (::rpc/profile-id params) profile-id)
(db/get-by-id conn :profile profile-id)
profile)]
(auth/verify-password password (:password profile)))
(ex/raise :type :authentication
:code :only-admins-allowed
:hint "only admins allowed to call this RPC method")))))

View file

@ -8,6 +8,7 @@
(:require (:require
[app.common.spec :as us] [app.common.spec :as us]
[app.db :as db] [app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.util.services :as sv] [app.util.services :as sv]
[clojure.spec.alpha :as s])) [clojure.spec.alpha :as s]))
@ -47,18 +48,18 @@
order by f.created_at asc") order by f.created_at asc")
(defn search-files (defn search-files
[conn {:keys [profile-id team-id search-term] :as params}] [conn {:keys [::rpc/profile-id team-id search-term] :as params}]
(db/exec! conn [sql:search-files (db/exec! conn [sql:search-files
profile-id team-id profile-id team-id
profile-id team-id profile-id team-id
search-term])) search-term]))
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::search-files ::us/string) (s/def ::search-files ::us/string)
(s/def ::search-files (s/def ::search-files
(s/keys :req-un [::profile-id ::team-id] (s/keys :req [::rpc/profile-id]
:req-un [::team-id]
:opt-un [::search-term])) :opt-un [::search-term]))
(sv/defmethod ::search-files (sv/defmethod ::search-files

View file

@ -17,6 +17,7 @@
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.main :as-alias main] [app.main :as-alias main]
[app.media :as media] [app.media :as media]
[app.rpc :as-alias rpc]
[app.rpc.climit :as climit] [app.rpc.climit :as climit]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
@ -35,7 +36,6 @@
(s/def ::id ::us/uuid) (s/def ::id ::us/uuid)
(s/def ::name ::us/string) (s/def ::name ::us/string)
(s/def ::profile-id ::us/uuid)
(s/def ::file-id ::us/uuid) (s/def ::file-id ::us/uuid)
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
@ -78,11 +78,11 @@
(declare retrieve-teams) (declare retrieve-teams)
(s/def ::get-teams (s/def ::get-teams
(s/keys :req-un [::profile-id])) (s/keys :req [::rpc/profile-id]))
(sv/defmethod ::get-teams (sv/defmethod ::get-teams
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id]}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(retrieve-teams conn profile-id))) (retrieve-teams conn profile-id)))
@ -122,11 +122,12 @@
(declare retrieve-team) (declare retrieve-team)
(s/def ::get-team (s/def ::get-team
(s/keys :req-un [::profile-id ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::id]))
(sv/defmethod ::get-team (sv/defmethod ::get-team
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id id]}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(retrieve-team conn profile-id id))) (retrieve-team conn profile-id id)))
@ -161,11 +162,12 @@
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::get-team-members (s/def ::get-team-members
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id]))
(sv/defmethod ::get-team-members (sv/defmethod ::get-team-members
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id]}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id team-id) (check-read-permissions! conn profile-id team-id)
(retrieve-team-members conn team-id))) (retrieve-team-members conn team-id)))
@ -177,13 +179,13 @@
(declare retrieve-team-for-file) (declare retrieve-team-for-file)
(s/def ::get-team-users (s/def ::get-team-users
(s/and (s/keys :req-un [::profile-id] (s/and (s/keys :req [::rpc/profile-id]
:opt-un [::team-id ::file-id]) :opt-un [::team-id ::file-id])
#(or (:team-id %) (:file-id %)))) #(or (:team-id %) (:file-id %))))
(sv/defmethod ::get-team-users (sv/defmethod ::get-team-users
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id file-id]}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id file-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(if team-id (if team-id
(do (do
@ -236,11 +238,12 @@
(declare retrieve-team-stats) (declare retrieve-team-stats)
(s/def ::get-team-stats (s/def ::get-team-stats
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id]))
(sv/defmethod ::get-team-stats (sv/defmethod ::get-team-stats
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id]}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id team-id) (check-read-permissions! conn profile-id team-id)
(retrieve-team-stats conn team-id))) (retrieve-team-stats conn team-id)))
@ -257,7 +260,8 @@
;; --- Query: Team invitations ;; --- Query: Team invitations
(s/def ::get-team-invitations (s/def ::get-team-invitations
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id]))
(def sql:team-invitations (def sql:team-invitations
"select email_to as email, role, (valid_until < now()) as expired "select email_to as email, role, (valid_until < now()) as expired
@ -270,7 +274,7 @@
(sv/defmethod ::get-team-invitations (sv/defmethod ::get-team-invitations
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id]}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id team-id) (check-read-permissions! conn profile-id team-id)
(get-team-invitations conn team-id))) (get-team-invitations conn team-id)))
@ -285,14 +289,15 @@
(declare ^:private create-team-default-project) (declare ^:private create-team-default-project)
(s/def ::create-team (s/def ::create-team
(s/keys :req-un [::profile-id ::name] (s/keys :req [::rpc/profile-id]
:req-un [::name]
:opt-un [::id])) :opt-un [::id]))
(sv/defmethod ::create-team (sv/defmethod ::create-team
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} params] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(create-team conn params))) (create-team conn (assoc params :profile-id profile-id))))
(defn create-team (defn create-team
"This is a complete team creation process, it creates the team "This is a complete team creation process, it creates the team
@ -316,22 +321,20 @@
:is-default is-default}))) :is-default is-default})))
(defn- create-team-role (defn- create-team-role
[conn {:keys [team-id profile-id role] :as params}] [conn {:keys [profile-id team-id role] :as params}]
(let [params {:team-id team-id (let [params {:team-id team-id
:profile-id profile-id}] :profile-id profile-id}]
(->> (perms/assign-role-flags params role) (->> (perms/assign-role-flags params role)
(db/insert! conn :team-profile-rel)))) (db/insert! conn :team-profile-rel))))
(defn- create-team-default-project (defn- create-team-default-project
[conn {:keys [team-id profile-id] :as params}] [conn {:keys [profile-id team-id] :as params}]
(let [project {:id (uuid/next) (let [project {:id (uuid/next)
:team-id team-id :team-id team-id
:name "Drafts" :name "Drafts"
:is-default true} :is-default true}
project (create-project conn project)] project (create-project conn project)]
(create-project-role conn {:project-id (:id project) (create-project-role conn profile-id (:id project) :owner)
:profile-id profile-id
:role :owner})
project)) project))
;; NOTE: we have project creation here because there are cyclic ;; NOTE: we have project creation here because there are cyclic
@ -351,7 +354,7 @@
:is-default is-default}))) :is-default is-default})))
(defn create-project-role (defn create-project-role
[conn {:keys [project-id profile-id role]}] [conn profile-id project-id role]
(let [params {:project-id project-id (let [params {:project-id project-id
:profile-id profile-id}] :profile-id profile-id}]
(->> (perms/assign-role-flags params role) (->> (perms/assign-role-flags params role)
@ -360,11 +363,12 @@
;; --- Mutation: Update Team ;; --- Mutation: Update Team
(s/def ::update-team (s/def ::update-team
(s/keys :req-un [::profile-id ::name ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::name ::id]))
(sv/defmethod ::update-team (sv/defmethod ::update-team
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [id name profile-id] :as params}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id name] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(check-edition-permissions! conn profile-id id) (check-edition-permissions! conn profile-id id)
(db/update! conn :team (db/update! conn :team
@ -379,11 +383,12 @@
(s/def ::reassign-to ::us/uuid) (s/def ::reassign-to ::us/uuid)
(s/def ::leave-team (s/def ::leave-team
(s/keys :req-un [::profile-id ::id] (s/keys :req [::rpc/profile-id]
:req-un [::id]
:opt-un [::reassign-to])) :opt-un [::reassign-to]))
(defn leave-team (defn leave-team
[conn {:keys [id profile-id reassign-to]}] [conn {:keys [::rpc/profile-id id reassign-to]}]
(let [perms (get-permissions conn profile-id id) (let [perms (get-permissions conn profile-id id)
members (retrieve-team-members conn id)] members (retrieve-team-members conn id)]
@ -438,7 +443,8 @@
;; --- Mutation: Delete Team ;; --- Mutation: Delete Team
(s/def ::delete-team (s/def ::delete-team
(s/keys :req-un [::profile-id ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::id]))
;; TODO: right now just don't allow delete default team, in future it ;; TODO: right now just don't allow delete default team, in future it
;; should raise a specific exception for signal that this action is ;; should raise a specific exception for signal that this action is
@ -446,7 +452,7 @@
(sv/defmethod ::delete-team (sv/defmethod ::delete-team
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [perms (get-permissions conn profile-id id)] (let [perms (get-permissions conn profile-id id)]
(when-not (:is-owner perms) (when-not (:is-owner perms)
@ -477,7 +483,7 @@
:viewer {:is-owner false :is-admin false :can-edit false})) :viewer {:is-owner false :is-admin false :can-edit false}))
(defn update-team-member-role (defn update-team-member-role
[conn {:keys [team-id profile-id member-id role] :as params}] [conn {:keys [profile-id team-id member-id role] :as params}]
;; We retrieve all team members instead of query the ;; We retrieve all team members instead of query the
;; database for a single member. This is just for ;; database for a single member. This is just for
;; convenience, if this becomes a bottleneck or problematic, ;; convenience, if this becomes a bottleneck or problematic,
@ -524,23 +530,25 @@
nil))) nil)))
(s/def ::update-team-member-role (s/def ::update-team-member-role
(s/keys :req-un [::profile-id ::team-id ::member-id ::role])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::member-id ::role]))
(sv/defmethod ::update-team-member-role (sv/defmethod ::update-team-member-role
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} params] [{:keys [::db/pool] :as cfg} params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(update-team-member-role conn params))) (update-team-member-role conn (assoc params :profile-id (::rpc/profile-id params)))))
;; --- Mutation: Delete Team Member ;; --- Mutation: Delete Team Member
(s/def ::delete-team-member (s/def ::delete-team-member
(s/keys :req-un [::profile-id ::team-id ::member-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::member-id]))
(sv/defmethod ::delete-team-member (sv/defmethod ::delete-team-member
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [team-id profile-id member-id] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id member-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [perms (get-permissions conn profile-id team-id)] (let [perms (get-permissions conn profile-id team-id)]
(when-not (or (:is-owner perms) (when-not (or (:is-owner perms)
@ -564,15 +572,16 @@
(s/def ::file ::media/upload) (s/def ::file ::media/upload)
(s/def ::update-team-photo (s/def ::update-team-photo
(s/keys :req-un [::profile-id ::team-id ::file])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::file]))
(sv/defmethod ::update-team-photo (sv/defmethod ::update-team-photo
{::doc/added "1.17"} {::doc/added "1.17"}
[cfg {:keys [file] :as params}] [cfg {:keys [::rpc/profile-id file] :as params}]
;; Validate incoming mime type ;; Validate incoming mime type
(media/validate-media-type! file #{"image/jpeg" "image/png" "image/webp"}) (media/validate-media-type! file #{"image/jpeg" "image/png" "image/webp"})
(let [cfg (update cfg :storage media/configure-assets-storage)] (let [cfg (update cfg :storage media/configure-assets-storage)]
(update-team-photo cfg params))) (update-team-photo cfg (assoc params :profile-id profile-id))))
(defn update-team-photo (defn update-team-photo
[{:keys [pool storage executor] :as cfg} {:keys [profile-id team-id] :as params}] [{:keys [pool storage executor] :as cfg} {:keys [profile-id team-id] :as params}]
@ -632,7 +641,7 @@
update set role = ?, updated_at = now();") update set role = ?, updated_at = now();")
(defn- create-invitation-token (defn- create-invitation-token
[cfg {:keys [valid-until profile-id team-id member-id member-email role]}] [cfg {:keys [profile-id valid-until team-id member-id member-email role]}]
(tokens/generate (::main/props cfg) (tokens/generate (::main/props cfg)
{:iss :team-invitation {:iss :team-invitation
:exp valid-until :exp valid-until
@ -715,14 +724,15 @@
(s/def ::email ::us/email) (s/def ::email ::us/email)
(s/def ::emails ::us/set-of-valid-emails) (s/def ::emails ::us/set-of-valid-emails)
(s/def ::create-team-invitations (s/def ::create-team-invitations
(s/keys :req-un [::profile-id ::team-id ::role] (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::role]
:opt-un [::email ::emails])) :opt-un [::email ::emails]))
(sv/defmethod ::create-team-invitations (sv/defmethod ::create-team-invitations
"A rpc call that allow to send a single or multiple invitations to "A rpc call that allow to send a single or multiple invitations to
join the team." join the team."
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id team-id email emails role] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id email emails role] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [perms (get-permissions conn profile-id team-id) (let [perms (get-permissions conn profile-id team-id)
profile (db/get-by-id conn :profile profile-id) profile (db/get-by-id conn :profile profile-id)
@ -760,7 +770,7 @@
(sv/defmethod ::create-team-with-invitations (sv/defmethod ::create-team-with-invitations
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id emails role] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id emails role] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [team (create-team conn params) (let [team (create-team conn params)
profile (db/get-by-id conn :profile profile-id) profile (db/get-by-id conn :profile profile-id)
@ -791,11 +801,12 @@
;; --- Query: get-team-invitation-token ;; --- Query: get-team-invitation-token
(s/def ::get-team-invitation-token (s/def ::get-team-invitation-token
(s/keys :req-un [::profile-id ::team-id ::email])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::email]))
(sv/defmethod ::get-team-invitation-token (sv/defmethod ::get-team-invitation-token
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [profile-id team-id email] :as params}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id email] :as params}]
(check-read-permissions! pool profile-id team-id) (check-read-permissions! pool profile-id team-id)
(let [invit (-> (db/get pool :team-invitation (let [invit (-> (db/get pool :team-invitation
{:team-id team-id {:team-id team-id
@ -813,11 +824,12 @@
;; --- Mutation: Update invitation role ;; --- Mutation: Update invitation role
(s/def ::update-team-invitation-role (s/def ::update-team-invitation-role
(s/keys :req-un [::profile-id ::team-id ::email ::role])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::email ::role]))
(sv/defmethod ::update-team-invitation-role (sv/defmethod ::update-team-invitation-role
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id team-id email role] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id email role] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [perms (get-permissions conn profile-id team-id)] (let [perms (get-permissions conn profile-id team-id)]
@ -833,11 +845,12 @@
;; --- Mutation: Delete invitation ;; --- Mutation: Delete invitation
(s/def ::delete-team-invitation (s/def ::delete-team-invitation
(s/keys :req-un [::profile-id ::team-id ::email])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::email]))
(sv/defmethod ::delete-team-invitation (sv/defmethod ::delete-team-invitation
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [pool] :as cfg} {:keys [profile-id team-id email] :as params}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id email] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [perms (get-permissions conn profile-id team-id)] (let [perms (get-permissions conn profile-id team-id)]

View file

@ -11,6 +11,7 @@
[app.db :as db] [app.db :as db]
[app.http.session :as session] [app.http.session :as session]
[app.loggers.audit :as audit] [app.loggers.audit :as audit]
[app.rpc :as-alias rpc]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
@ -27,10 +28,10 @@
(s/def ::verify-token (s/def ::verify-token
(s/keys :req-un [::token] (s/keys :req-un [::token]
:opt-un [::profile-id])) :opt [::rpc/profile-id]))
(sv/defmethod ::verify-token (sv/defmethod ::verify-token
{:auth false {::rpc/auth false
::doc/added "1.15"} ::doc/added "1.15"}
[{:keys [pool sprops] :as cfg} {:keys [token] :as params}] [{:keys [pool sprops] :as cfg} {:keys [token] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
@ -126,7 +127,8 @@
:opt-un [::spec.team-invitation/member-id])) :opt-un [::spec.team-invitation/member-id]))
(defmethod process-token :team-invitation (defmethod process-token :team-invitation
[{:keys [conn session] :as cfg} {:keys [profile-id token]} [{:keys [conn session] :as cfg}
{:keys [::rpc/profile-id token]}
{:keys [member-id team-id member-email] :as claims}] {:keys [member-id team-id member-email] :as claims}]
(us/verify! ::team-invitation-claims claims) (us/verify! ::team-invitation-claims claims)

View file

@ -8,6 +8,7 @@
(:require (:require
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.db :as db] [app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.commands.comments :as comments] [app.rpc.commands.comments :as comments]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.cond :as-alias cond] [app.rpc.cond :as-alias cond]
@ -73,16 +74,16 @@
(s/def ::get-view-only-bundle (s/def ::get-view-only-bundle
(s/keys :req-un [::files/file-id] (s/keys :req-un [::files/file-id]
:opt-un [::files/profile-id :opt-un [::files/share-id
::files/share-id ::files/features]
::files/features])) :opt [::rpc/profile-id]))
(sv/defmethod ::get-view-only-bundle (sv/defmethod ::get-view-only-bundle
{:auth false {::rpc/auth false
::cond/get-object #(files/get-minimal-file %1 (:file-id %2)) ::cond/get-object #(files/get-minimal-file %1 (:file-id %2))
::cond/key-fn files/get-file-etag ::cond/key-fn files/get-file-etag
::cond/reuse-key? true ::cond/reuse-key? true
::doc/added "1.17"} ::doc/added "1.17"}
[{:keys [pool]} params] [{:keys [pool]} params]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(get-view-only-bundle conn params))) (get-view-only-bundle conn (assoc params :profile-id (::rpc/profile-id params)))))

View file

@ -12,6 +12,7 @@
[app.db :as db] [app.db :as db]
[app.http.client :as http] [app.http.client :as http]
[app.loggers.webhooks :as webhooks] [app.loggers.webhooks :as webhooks]
[app.rpc :as-alias rpc]
[app.rpc.commands.teams :refer [check-edition-permissions! check-read-permissions!]] [app.rpc.commands.teams :refer [check-edition-permissions! check-read-permissions!]]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.util.services :as sv] [app.util.services :as sv]
@ -23,7 +24,6 @@
;; --- Mutation: Create Webhook ;; --- Mutation: Create Webhook
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::uri ::us/not-empty-string) (s/def ::uri ::us/not-empty-string)
(s/def ::is-active ::us/boolean) (s/def ::is-active ::us/boolean)
@ -33,7 +33,8 @@
"application/transit+json"}) "application/transit+json"})
(s/def ::create-webhook (s/def ::create-webhook
(s/keys :req-un [::profile-id ::team-id ::uri ::mtype] (s/keys :req [::rpc/profile-id]
:req-un [::team-id ::uri ::mtype]
:opt-un [::is-active])) :opt-un [::is-active]))
;; NOTE: for now the quote is hardcoded but this need to be solved in ;; NOTE: for now the quote is hardcoded but this need to be solved in
@ -98,7 +99,7 @@
(sv/defmethod ::create-webhook (sv/defmethod ::create-webhook
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [profile-id team-id] :as params}] [{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [::rpc/profile-id team-id] :as params}]
(check-edition-permissions! pool profile-id team-id) (check-edition-permissions! pool profile-id team-id)
(validate-quotes! cfg params) (validate-quotes! cfg params)
(->> (validate-webhook! cfg nil params) (->> (validate-webhook! cfg nil params)
@ -109,18 +110,19 @@
(sv/defmethod ::update-webhook (sv/defmethod ::update-webhook
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [id profile-id] :as params}] [{:keys [::db/pool ::wrk/executor] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(let [whook (db/get pool :webhook {:id id})] (let [whook (db/get pool :webhook {:id id})]
(check-edition-permissions! pool profile-id (:team-id whook)) (check-edition-permissions! pool profile-id (:team-id whook))
(->> (validate-webhook! cfg whook params) (->> (validate-webhook! cfg whook params)
(p/fmap executor (fn [_] (update-webhook! cfg whook params)))))) (p/fmap executor (fn [_] (update-webhook! cfg whook params))))))
(s/def ::delete-webhook (s/def ::delete-webhook
(s/keys :req-un [::profile-id ::id])) (s/keys :req [::rpc/profile-id]
:req-un [::id]))
(sv/defmethod ::delete-webhook (sv/defmethod ::delete-webhook
{::doc/added "1.17"} {::doc/added "1.17"}
[{:keys [::db/pool] :as cfg} {:keys [profile-id id]}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id id]}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [whook (db/get conn :webhook {:id id})] (let [whook (db/get conn :webhook {:id id})]
(check-edition-permissions! conn profile-id (:team-id whook)) (check-edition-permissions! conn profile-id (:team-id whook))
@ -131,14 +133,15 @@
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::get-webhooks (s/def ::get-webhooks
(s/keys :req-un [::profile-id ::team-id])) (s/keys :req [::rpc/profile-id]
:req-un [::team-id]))
(def sql:get-webhooks (def sql:get-webhooks
"select id, uri, mtype, is_active, error_code, error_count "select id, uri, mtype, is_active, error_code, error_count
from webhook where team_id = ? order by uri") from webhook where team_id = ? order by uri")
(sv/defmethod ::get-webhooks (sv/defmethod ::get-webhooks
[{:keys [pool] :as cfg} {:keys [profile-id team-id]}] [{:keys [pool] :as cfg} {:keys [::rpc/profile-id team-id]}]
(with-open [conn (db/open pool)] (with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id team-id) (check-read-permissions! conn profile-id team-id)
(db/exec! conn [sql:get-webhooks team-id]))) (db/exec! conn [sql:get-webhooks team-id])))

View file

@ -81,7 +81,8 @@
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(cmd.files/check-edition-permissions! conn profile-id id) (cmd.files/check-edition-permissions! conn profile-id id)
(cmd.files/absorb-library conn params) (cmd.files/absorb-library conn params)
(cmd.files/mark-file-deleted conn params))) (cmd.files/mark-file-deleted conn params)
nil))
;; --- Mutation: Link file to library ;; --- Mutation: Link file to library

View file

@ -6,6 +6,7 @@
(ns app.rpc.mutations.profile (ns app.rpc.mutations.profile
(:require (:require
[app.auth :as auth]
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.spec :as us] [app.common.spec :as us]
@ -17,7 +18,7 @@
[app.media :as media] [app.media :as media]
[app.rpc :as-alias rpc] [app.rpc :as-alias rpc]
[app.rpc.climit :as-alias climit] [app.rpc.climit :as-alias climit]
[app.rpc.commands.auth :as auth] [app.rpc.commands.auth :as cmd.auth]
[app.rpc.commands.teams :as teams] [app.rpc.commands.teams :as teams]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.rpc.helpers :as rph] [app.rpc.helpers :as rph]
@ -182,7 +183,7 @@
(defn- change-email-immediately (defn- change-email-immediately
[{:keys [conn]} {:keys [profile email] :as params}] [{:keys [conn]} {:keys [profile email] :as params}]
(when (not= email (:email profile)) (when (not= email (:email profile))
(auth/check-profile-existence! conn params)) (cmd.auth/check-profile-existence! conn params))
(db/update! conn :profile (db/update! conn :profile
{:email email} {:email email}
{:id (:id profile)}) {:id (:id profile)})
@ -201,7 +202,7 @@
:exp (dt/in-future {:days 30})})] :exp (dt/in-future {:days 30})})]
(when (not= email (:email profile)) (when (not= email (:email profile))
(auth/check-profile-existence! conn params)) (cmd.auth/check-profile-existence! conn params))
(when-not (eml/allow-send-emails? conn profile) (when-not (eml/allow-send-emails? conn profile)
(ex/raise :type :validation (ex/raise :type :validation

View file

@ -26,8 +26,6 @@
;; --- Mutation: Create Project ;; --- Mutation: Create Project
(declare create-project-profile-state)
(s/def ::team-id ::us/uuid) (s/def ::team-id ::us/uuid)
(s/def ::create-project (s/def ::create-project
(s/keys :req-un [::profile-id ::team-id ::name] (s/keys :req-un [::profile-id ::team-id ::name]
@ -39,21 +37,16 @@
[{:keys [pool] :as cfg} {:keys [profile-id team-id] :as params}] [{:keys [pool] :as cfg} {:keys [profile-id team-id] :as params}]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(teams/check-edition-permissions! conn profile-id team-id) (teams/check-edition-permissions! conn profile-id team-id)
(let [project (teams/create-project conn params) (let [project (teams/create-project conn params)]
params (assoc params (teams/create-project-role conn profile-id (:id project) :owner)
:project-id (:id project)
:role :owner)]
(teams/create-project-role conn params)
(create-project-profile-state conn params)
(assoc project :is-pinned true))))
(defn create-project-profile-state
[conn {:keys [team-id project-id profile-id] :as params}]
(db/insert! conn :team-project-profile-rel (db/insert! conn :team-project-profile-rel
{:project-id project-id {:project-id (:id project)
:profile-id profile-id :profile-id profile-id
:team-id team-id :team-id team-id
:is-pinned true})) :is-pinned true})
(assoc project :is-pinned true))))
;; --- Mutation: Toggle Project Pin ;; --- Mutation: Toggle Project Pin

View file

@ -10,6 +10,7 @@
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.rpc :as-alias rpc]
[app.util.services :as sv] [app.util.services :as sv]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -36,7 +37,7 @@
(s/keys :opt-un [::profile-id])) (s/keys :opt-un [::profile-id]))
(sv/defmethod ::profile (sv/defmethod ::profile
{:auth false} {::rpc/auth false}
[{:keys [pool] :as cfg} {:keys [profile-id] :as params}] [{:keys [pool] :as cfg} {:keys [profile-id] :as params}]
;; We need to return the anonymous profile object in two cases, when ;; We need to return the anonymous profile object in two cases, when
;; no profile-id is in session, and when db call raises not found. In all other ;; no profile-id is in session, and when db call raises not found. In all other

View file

@ -8,6 +8,7 @@
(:require (:require
[app.common.spec :as us] [app.common.spec :as us]
[app.db :as db] [app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.commands.viewer :as viewer] [app.rpc.commands.viewer :as viewer]
[app.rpc.doc :as-alias doc] [app.rpc.doc :as-alias doc]
[app.util.services :as sv] [app.util.services :as sv]
@ -19,7 +20,7 @@
(s/keys :opt-un [::components-v2]))) (s/keys :opt-un [::components-v2])))
(sv/defmethod ::view-only-bundle (sv/defmethod ::view-only-bundle
{:auth false {::rpc/auth false
::doc/added "1.3" ::doc/added "1.3"
::doc/deprecated "1.17"} ::doc/deprecated "1.17"}
[{:keys [pool] :as cfg} {:keys [features components-v2] :as params}] [{:keys [pool] :as cfg} {:keys [features components-v2] :as params}]

View file

@ -13,6 +13,7 @@
[app.db :as db] [app.db :as db]
[app.main :as-alias main] [app.main :as-alias main]
[app.setup.builtin-templates] [app.setup.builtin-templates]
[app.setup.initial-user]
[app.setup.keys :as keys] [app.setup.keys :as keys]
[buddy.core.codecs :as bc] [buddy.core.codecs :as bc]
[buddy.core.nonce :as bn] [buddy.core.nonce :as bn]

View file

@ -0,0 +1,40 @@
;; 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
(ns app.setup.initial-user
"Initial data setup of instance."
(:require
[app.auth :as auth]
[app.common.logging :as l]
[app.config :as cf]
[app.db :as db]
[app.setup :as-alias setup]
[clojure.spec.alpha :as s]
[integrant.core :as ig]))
(def ^:private sql:insert-profile
"insert into profile (id, fullname, email, password, is_active, is_admin, created_at, modified_at)
values ('00000000-0000-0000-0000-000000000000', 'Admin', ?, ?, true, true, now(), now())
on conflict (id)
do update set email = ?, password = ?")
(defmethod ig/pre-init-spec ::setup/initial-profile [_]
(s/keys :req [::db/pool]))
(defmethod ig/init-key ::setup/initial-profile
[_ {:keys [::db/pool]}]
(let [email (cf/get :setup-admin-email)
password (cf/get :setup-admin-password)]
(when (and email password)
(db/with-atomic [conn pool]
(let [pwd (auth/derive-password password)]
(db/exec-one! conn [sql:insert-profile email pwd email pwd])
(l/info :hint "setting initial user (admin)"
:email email
:password "********"))))
nil))

View file

@ -8,6 +8,7 @@
"A main namespace for server repl." "A main namespace for server repl."
#_:clj-kondo/ignore #_:clj-kondo/ignore
(:require (:require
[app.auth :refer [derive-password]]
[app.common.data :as d] [app.common.data :as d]
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.logging :as l] [app.common.logging :as l]
@ -20,7 +21,6 @@
[app.db :as db] [app.db :as db]
[app.db.sql :as sql] [app.db.sql :as sql]
[app.main :refer [system]] [app.main :refer [system]]
[app.rpc.commands.auth :refer [derive-password]]
[app.rpc.queries.profile :as prof] [app.rpc.queries.profile :as prof]
[app.util.blob :as blob] [app.util.blob :as blob]
[app.util.time :as dt] [app.util.time :as dt]

View file

@ -6,7 +6,7 @@
(ns app.tasks.objects-gc (ns app.tasks.objects-gc
"A maintenance task that performs a general purpose garbage collection "A maintenance task that performs a general purpose garbage collection
of deleted objects." of deleted or unreachable objects."
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.logging :as l] [app.common.logging :as l]
@ -16,154 +16,247 @@
[app.storage :as sto] [app.storage :as sto]
[app.util.time :as dt] [app.util.time :as dt]
[clojure.spec.alpha :as s] [clojure.spec.alpha :as s]
[cuerdas.core :as str]
[integrant.core :as ig])) [integrant.core :as ig]))
(def target-tables (declare ^:private delete-profiles!)
["profile" (declare ^:private delete-teams!)
"team" (declare ^:private delete-fonts!)
"file" (declare ^:private delete-projects!)
"project" (declare ^:private delete-files!)
"team_font_variant"]) (declare ^:private delete-orphan-teams!)
(defmulti delete-objects :table)
(def sql:delete-objects
"with deleted as (
select id from %(table)s
where deleted_at is not null
and deleted_at < now() - ?::interval
order by deleted_at
limit %(limit)s
)
delete from %(table)s
where id in (select id from deleted)
returning *")
;; --- IMPL: generic object deletion
(defmethod delete-objects :default
[{:keys [conn min-age table] :as cfg}]
(let [sql (str/fmt sql:delete-objects
{:table table :limit 50})
result (db/exec! conn [sql min-age])]
(doseq [{:keys [id] :as item} result]
(l/debug :hint "permanently delete object" :table table :id id))
(count result)))
;; --- IMPL: file deletion
(defmethod delete-objects "file"
[{:keys [conn min-age table] :as cfg}]
(let [sql (str/fmt sql:delete-objects {:table table :limit 50})
result (db/exec! conn [sql min-age])]
(doseq [{:keys [id] :as item} result]
(l/debug :hint "permanently delete object" :table table :id id))
(count result)))
;; --- IMPL: team-font-variant deletion
(defmethod delete-objects "team_font_variant"
[{:keys [conn min-age storage table] :as cfg}]
(let [sql (str/fmt sql:delete-objects {:table table :limit 50})
fonts (db/exec! conn [sql min-age])
storage (media/configure-assets-storage storage conn)]
(doseq [{:keys [id] :as font} fonts]
(l/debug :hint "permanently delete object" :table table :id id)
(some->> (:woff1-file-id font) (sto/touch-object! storage) deref)
(some->> (:woff2-file-id font) (sto/touch-object! storage) deref)
(some->> (:otf-file-id font) (sto/touch-object! storage) deref)
(some->> (:ttf-file-id font) (sto/touch-object! storage) deref))
(count fonts)))
;; --- IMPL: team deletion
(defmethod delete-objects "team"
[{:keys [conn min-age storage table] :as cfg}]
(let [sql (str/fmt sql:delete-objects {:table table :limit 50})
teams (db/exec! conn [sql min-age])
storage (media/configure-assets-storage storage conn)]
(doseq [{:keys [id] :as team} teams]
(l/debug :hint "permanently delete object" :table table :id id)
(some->> (:photo-id team) (sto/touch-object! storage) deref))
(count teams)))
;; --- IMPL: profile deletion
(def sql:retrieve-deleted-profiles
"select id, photo_id from profile
where deleted_at is not null
and deleted_at < now() - ?::interval
order by deleted_at
limit ?
for update")
(defmethod delete-objects "profile"
[{:keys [conn min-age storage table] :as cfg}]
(let [profiles (db/exec! conn [sql:retrieve-deleted-profiles min-age 50])
storage (media/configure-assets-storage storage conn)]
(doseq [{:keys [id] :as profile} profiles]
(l/debug :hint "permanently delete object" :table table :id id)
;; Mark as deleted the storage object related with the photo-id
;; field.
(some->> (:photo-id profile) (sto/touch-object! storage) deref)
;; And finally, permanently delete the profile.
(db/delete! conn :profile {:id id}))
(count profiles)))
;; --- INIT
(defn- process-table
[{:keys [table] :as cfg}]
(loop [n 0]
(let [res (delete-objects cfg)]
(if (pos? res)
(recur (+ n res))
(do
(l/debug :hint "delete summary" :table table :total n)
n)))))
(s/def ::min-age ::dt/duration) (s/def ::min-age ::dt/duration)
(defmethod ig/pre-init-spec ::handler [_] (defmethod ig/pre-init-spec ::handler [_]
(s/keys :req-un [::db/pool ::sto/storage] (s/keys :req [::db/pool ::sto/storage]
:opt-un [::min-age])) :opt [::min-age]))
(defmethod ig/prep-key ::handler (defmethod ig/prep-key ::handler
[_ cfg] [_ cfg]
(merge {:min-age cf/deletion-delay} (merge {::min-age cf/deletion-delay}
(d/without-nils cfg))) (d/without-nils cfg)))
(defmethod ig/init-key ::handler (defmethod ig/init-key ::handler
[_ {:keys [pool] :as cfg}] [_ {:keys [::db/pool ::sto/storage] :as cfg}]
(fn [params] (fn [params]
(db/with-atomic [conn pool] (db/with-atomic [conn pool]
(let [min-age (or (:min-age params) (:min-age cfg)) (let [min-age (or (:min-age params) (::min-age cfg))
cfg (-> cfg _ (l/info :hint "gc started"
(assoc :min-age (db/interval min-age))
(assoc :conn conn))]
(loop [tables (seq target-tables)
total 0]
(if-let [table (first tables)]
(recur (rest tables)
(+ total (process-table (assoc cfg :table table))))
(do
(l/info :hint "objects gc finished successfully"
:min-age (dt/format-duration min-age) :min-age (dt/format-duration min-age)
:total total) :rollback? (boolean (:rollback? params)))
storage (media/configure-assets-storage storage conn)
cfg (-> cfg
(assoc ::min-age (db/interval min-age))
(assoc ::conn conn)
(assoc ::storage storage))
htotal (+ (delete-profiles! cfg)
(delete-teams! cfg)
(delete-projects! cfg)
(delete-files! cfg)
(delete-fonts! cfg))
stotal (delete-orphan-teams! cfg)]
(l/info :hint "gc finished"
:deleted htotal
:orphans stotal
:rollback? (boolean (:rollback? params)))
(when (:rollback? params) (when (:rollback? params)
(db/rollback! conn)) (db/rollback! conn))
{:processed total}))))))) {:processed (+ stotal htotal)}))))
(def ^:private sql:get-profiles-chunk
"select id, photo_id, created_at from profile
where deleted_at is not null
and deleted_at < now() - ?::interval
and created_at < ?
order by created_at desc
limit 10
for update skip locked")
(defn- delete-profiles!
[{:keys [::conn ::min-age ::storage] :as cfg}]
(letfn [(get-chunk [cursor]
(let [rows (db/exec! conn [sql:get-profiles-chunk min-age cursor])]
[(some->> rows peek :created-at) rows]))]
(reduce
(fn [total {:keys [id photo-id]}]
(l/debug :hint "permanently delete profile" :id (str id))
;; Mark as deleted the storage object related with the
;; photo-id field.
(some->> photo-id (sto/touch-object! storage) deref)
;; And finally, permanently delete the profile.
(db/delete! conn :profile {:id id})
(inc total))
0
(d/iteration get-chunk
:vf second
:kf first
:initk (dt/now)))))
(def ^:private sql:get-teams-chunk
"select id, photo_id, created_at from team
where deleted_at is not null
and deleted_at < now() - ?::interval
and created_at < ?
order by created_at desc
limit 10
for update skip locked")
(defn- delete-teams!
[{:keys [::conn ::min-age ::storage] :as cfg}]
(letfn [(get-chunk [cursor]
(let [rows (db/exec! conn [sql:get-teams-chunk min-age cursor])]
[(some->> rows peek :created-at) rows]))]
(reduce
(fn [total {:keys [id photo-id]}]
(l/debug :hint "permanently delete team" :id (str id))
;; Mark as deleted the storage object related with the
;; photo-id field.
(some->> photo-id (sto/touch-object! storage) deref)
;; And finally, permanently delete the team.
(db/delete! conn :team {:id id})
(inc total))
0
(d/iteration get-chunk
:vf second
:kf first
:initk (dt/now)))))
(def ^:private sql:get-orphan-teams-chunk
"select t.id, t.created_at
from team as t
left join team_profile_rel as tpr
on (t.id = tpr.team_id)
where tpr.profile_id is null
and t.created_at < ?
order by t.created_at desc
limit 10
for update of t skip locked;")
(defn- delete-orphan-teams!
"Find all orphan teams (with no members and mark them for
deletion (soft delete)."
[{:keys [::conn] :as cfg}]
(letfn [(get-chunk [cursor]
(let [rows (db/exec! conn [sql:get-orphan-teams-chunk cursor])]
[(some->> rows peek :created-at) rows]))]
(reduce
(fn [total {:keys [id]}]
(l/debug :hint "mark team for deletion" :id (str id))
;; And finally, permanently delete the team.
(db/update! conn :team
{:deleted-at (dt/now)}
{:id id})
(inc total))
0
(d/iteration get-chunk
:vf second
:kf first
:initk (dt/now)))))
(def ^:private sql:get-fonts-chunk
"select id, created_at, woff1_file_id, woff2_file_id, otf_file_id, ttf_file_id
from team_font_variant
where deleted_at is not null
and deleted_at < now() - ?::interval
and created_at < ?
order by created_at desc
limit 10
for update skip locked")
(defn- delete-fonts!
[{:keys [::conn ::min-age ::storage] :as cfg}]
(letfn [(get-chunk [cursor]
(let [rows (db/exec! conn [sql:get-fonts-chunk min-age cursor])]
[(some->> rows peek :created-at) rows]))]
(reduce
(fn [total {:keys [id] :as font}]
(l/debug :hint "permanently delete font variant" :id (str id))
;; Mark as deleted the all related storage objects
(some->> (:woff1-file-id font) (sto/touch-object! storage) deref)
(some->> (:woff2-file-id font) (sto/touch-object! storage) deref)
(some->> (:otf-file-id font) (sto/touch-object! storage) deref)
(some->> (:ttf-file-id font) (sto/touch-object! storage) deref)
;; And finally, permanently delete the team font variant
(db/delete! conn :team-font-variant {:id id})
(inc total))
0
(d/iteration get-chunk
:vf second
:kf first
:initk (dt/now)))))
(def ^:private sql:get-projects-chunk
"select id, created_at
from project
where deleted_at is not null
and deleted_at < now() - ?::interval
and created_at < ?
order by created_at desc
limit 10
for update skip locked")
(defn- delete-projects!
[{:keys [::conn ::min-age] :as cfg}]
(letfn [(get-chunk [cursor]
(let [rows (db/exec! conn [sql:get-projects-chunk min-age cursor])]
[(some->> rows peek :created-at) rows]))]
(reduce
(fn [total {:keys [id]}]
(l/debug :hint "permanently delete project" :id (str id))
;; And finally, permanently delete the project.
(db/delete! conn :project {:id id})
(inc total))
0
(d/iteration get-chunk
:vf second
:kf first
:initk (dt/now)))))
(def ^:private sql:get-files-chunk
"select id, created_at
from file
where deleted_at is not null
and deleted_at < now() - ?::interval
and created_at < ?
order by created_at desc
limit 10
for update skip locked")
(defn- delete-files!
[{:keys [::conn ::min-age] :as cfg}]
(letfn [(get-chunk [cursor]
(let [rows (db/exec! conn [sql:get-files-chunk min-age cursor])]
[(some->> rows peek :created-at) rows]))]
(reduce
(fn [total {:keys [id]}]
(l/debug :hint "permanently delete file" :id (str id))
;; And finally, permanently delete the file.
(db/delete! conn :file {:id id})
(inc total))
0
(d/iteration get-chunk
:vf second
:kf first
:initk (dt/now)))))

View file

@ -6,6 +6,7 @@
(ns backend-tests.helpers (ns backend-tests.helpers
(:require (:require
[app.auth]
[app.common.data :as d] [app.common.data :as d]
[app.common.flags :as flags] [app.common.flags :as flags]
[app.common.pages :as cp] [app.common.pages :as cp]
@ -17,6 +18,7 @@
[app.main :as main] [app.main :as main]
[app.media] [app.media]
[app.migrations] [app.migrations]
[app.rpc :as-alias rpc]
[app.rpc.commands.auth :as cmd.auth] [app.rpc.commands.auth :as cmd.auth]
[app.rpc.commands.files :as files] [app.rpc.commands.files :as files]
[app.rpc.commands.files.create :as files.create] [app.rpc.commands.files.create :as files.create]
@ -101,8 +103,9 @@
*pool* (:app.db/pool system)] *pool* (:app.db/pool system)]
(with-redefs [app.config/flags (flags/parse flags/default default-flags (:flags config)) (with-redefs [app.config/flags (flags/parse flags/default default-flags (:flags config))
app.config/config config app.config/config config
app.rpc.commands.auth/derive-password identity app.loggers.audit/submit! (constantly nil)
app.rpc.commands.auth/verify-password (fn [a b] {:valid (= a b)})] app.auth/derive-password identity
app.auth/verify-password (fn [a b] {:valid (= a b)})]
(next))) (next)))
(finally (finally
(ig/halt! system))))) (ig/halt! system)))))
@ -322,14 +325,21 @@
(try-on! (method-fn (dissoc data ::type))))) (try-on! (method-fn (dissoc data ::type)))))
(defn mutation! (defn mutation!
[{:keys [::type] :as data}] [{:keys [::type profile-id] :as data}]
(let [method-fn (get-in *system* [:app.rpc/methods :mutations type])] (let [method-fn (get-in *system* [:app.rpc/methods :mutations type])]
(try-on! (method-fn (dissoc data ::type))))) (try-on! (method-fn (-> data
(dissoc ::type)
(assoc ::rpc/profile-id profile-id)
(d/without-nils))))))
(defn query! (defn query!
[{:keys [::type] :as data}] [{:keys [::type profile-id] :as data}]
(let [method-fn (get-in *system* [:app.rpc/methods :queries type])] (let [method-fn (get-in *system* [:app.rpc/methods :queries type])]
(try-on! (method-fn (dissoc data ::type))))) (try-on! (method-fn (-> data
(dissoc ::type)
(assoc ::rpc/profile-id profile-id)
(d/without-nils))))))
(defn run-task! (defn run-task!
([name] ([name]

View file

@ -65,8 +65,7 @@
;; Refresh webhook ;; Refresh webhook
(let [whk' (th/db-get :webhook {:id (:id whk)})] (let [whk' (th/db-get :webhook {:id (:id whk)})]
(t/is (nil? (:error-code whk'))) (t/is (nil? (:error-code whk'))))
(prn whk'))
))) )))

View file

@ -10,6 +10,7 @@
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.util.time :as dt] [app.util.time :as dt]
[app.rpc :as-alias rpc]
[backend-tests.helpers :as th] [backend-tests.helpers :as th]
[clojure.test :as t])) [clojure.test :as t]))
@ -37,7 +38,7 @@
params {::th/type :push-audit-events params {::th/type :push-audit-events
:app.http/request http-request :app.http/request http-request
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:events [{:name "navigate" :events [{:name "navigate"
:props {:project-id proj-id :props {:project-id proj-id
:team-id team-id :team-id team-id
@ -67,7 +68,7 @@
params {::th/type :push-audit-events params {::th/type :push-audit-events
:app.http/request http-request :app.http/request http-request
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:events [{:name "navigate" :events [{:name "navigate"
:props {:project-id proj-id :props {:project-id proj-id
:team-id team-id :team-id team-id

View file

@ -6,12 +6,13 @@
(ns backend-tests.rpc-cond-middleware-test (ns backend-tests.rpc-cond-middleware-test
(:require (:require
[backend-tests.storage-test :refer [configure-storage-backend]]
[backend-tests.helpers :as th]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.http :as http] [app.http :as http]
[app.rpc :as-alias rpc]
[app.rpc.cond :as cond] [app.rpc.cond :as cond]
[backend-tests.helpers :as th]
[backend-tests.storage-test :refer [configure-storage-backend]]
[clojure.test :as t] [clojure.test :as t]
[datoteka.core :as fs])) [datoteka.core :as fs]))
@ -24,7 +25,9 @@
:profile-id (:id profile)}) :profile-id (:id profile)})
file1 (th/create-file* 1 {:profile-id (:id profile) file1 (th/create-file* 1 {:profile-id (:id profile)
:project-id (:id project)}) :project-id (:id project)})
params {::th/type :get-file :id (:id file1) :profile-id (:id profile)}] params {::th/type :get-file
:id (:id file1)
::rpc/profile-id (:id profile)}]
(binding [cond/*enabled* true] (binding [cond/*enabled* true]
(let [{:keys [error result]} (th/command! params)] (let [{:keys [error result]} (th/command! params)]

View file

@ -583,6 +583,7 @@
:object-id (str page-id frame1-id) :object-id (str page-id frame1-id)
:data nil} :data nil}
{:keys [error result] :as out} (th/mutation! data)] {:keys [error result] :as out} (th/mutation! data)]
;; (th/print-result! out)
(t/is (nil? error)) (t/is (nil? error))
(t/is (nil? result))) (t/is (nil? result)))

View file

@ -6,12 +6,13 @@
(ns backend-tests.rpc-management-test (ns backend-tests.rpc-management-test
(:require (:require
[backend-tests.storage-test :refer [configure-storage-backend]]
[backend-tests.helpers :as th]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.http :as http] [app.http :as http]
[app.rpc :as-alias rpc]
[app.storage :as sto] [app.storage :as sto]
[backend-tests.helpers :as th]
[backend-tests.storage-test :refer [configure-storage-backend]]
[buddy.core.bytes :as b] [buddy.core.bytes :as b]
[clojure.test :as t] [clojure.test :as t]
[datoteka.core :as fs])) [datoteka.core :as fs]))
@ -50,7 +51,7 @@
:object (select-keys mobj [:id :width :height :mtype :name])}]}) :object (select-keys mobj [:id :width :height :mtype :name])}]})
(let [data {::th/type :duplicate-file (let [data {::th/type :duplicate-file
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:file-id (:id file1) :file-id (:id file1)
:name "file 1 (copy)"} :name "file 1 (copy)"}
out (th/command! data)] out (th/command! data)]
@ -122,7 +123,7 @@
@(sto/del-object! storage sobject) @(sto/del-object! storage sobject)
(let [data {::th/type :duplicate-file (let [data {::th/type :duplicate-file
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:file-id (:id file1) :file-id (:id file1)
:name "file 1 (copy)"} :name "file 1 (copy)"}
out (th/command! data)] out (th/command! data)]
@ -184,7 +185,7 @@
(let [data {::th/type :duplicate-project (let [data {::th/type :duplicate-project
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project) :project-id (:id project)
:name "project 1 (copy)"} :name "project 1 (copy)"}
out (th/command! data)] out (th/command! data)]
@ -250,7 +251,7 @@
(th/mark-file-deleted* {:id (:id file1)}) (th/mark-file-deleted* {:id (:id file1)})
(let [data {::th/type :duplicate-project (let [data {::th/type :duplicate-project
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project) :project-id (:id project)
:name "project 1 (copy)"} :name "project 1 (copy)"}
out (th/command! data)] out (th/command! data)]
@ -313,7 +314,7 @@
;; Try to move to same project ;; Try to move to same project
(let [data {::th/type :move-files (let [data {::th/type :move-files
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project1) :project-id (:id project1)
:ids #{(:id file1)}} :ids #{(:id file1)}}
@ -333,7 +334,7 @@
;; move a file1 to project2 (in the same team) ;; move a file1 to project2 (in the same team)
(let [data {::th/type :move-files (let [data {::th/type :move-files
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project2) :project-id (:id project2)
:ids #{(:id file1)}} :ids #{(:id file1)}}
@ -416,7 +417,7 @@
;; move to other project in other team ;; move to other project in other team
(let [data {::th/type :move-files (let [data {::th/type :move-files
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project2) :project-id (:id project2)
:ids #{(:id file1)}} :ids #{(:id file1)}}
out (th/command! data)] out (th/command! data)]
@ -489,7 +490,7 @@
;; move the library to other project ;; move the library to other project
(let [data {::th/type :move-files (let [data {::th/type :move-files
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project2) :project-id (:id project2)
:ids #{(:id file2)}} :ids #{(:id file2)}}
out (th/command! data)] out (th/command! data)]
@ -575,7 +576,7 @@
;; move project1 to other team ;; move project1 to other team
;; TODO: correct team change of project ;; TODO: correct team change of project
(let [data {::th/type :move-project (let [data {::th/type :move-project
:profile-id (:id profile) ::rpc/profile-id (:id profile)
:project-id (:id project1) :project-id (:id project1)
:team-id (:id team)} :team-id (:id team)}
out (th/command! data)] out (th/command! data)]
@ -608,7 +609,7 @@
(t/deftest clone-template (t/deftest clone-template
(let [prof (th/create-profile* 1 {:is-active true}) (let [prof (th/create-profile* 1 {:is-active true})
data {::th/type :clone-template data {::th/type :clone-template
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:project-id (:default-project-id prof) :project-id (:default-project-id prof)
:template-id "test"} :template-id "test"}
@ -624,7 +625,7 @@
(t/deftest retrieve-list-of-buitin-templates (t/deftest retrieve-list-of-buitin-templates
(let [prof (th/create-profile* 1 {:is-active true}) (let [prof (th/create-profile* 1 {:is-active true})
data {::th/type :retrieve-list-of-builtin-templates data {::th/type :retrieve-list-of-builtin-templates
:profile-id (:id prof)} ::rpc/profile-id (:id prof)}
out (th/command! data)] out (th/command! data)]
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (nil? (:error out))) (t/is (nil? (:error out)))

View file

@ -146,7 +146,12 @@
;; execute permanent deletion task ;; execute permanent deletion task
(let [result (th/run-task! :objects-gc {:min-age (dt/duration "-1m")})] (let [result (th/run-task! :objects-gc {:min-age (dt/duration "-1m")})]
(t/is (= 1 (:processed result)))) (t/is (= 2 (:processed result))))
(let [row (th/db-get :team
{:id (:default-team-id prof)}
{:check-deleted? false})]
(t/is (dt/instant? (:deleted-at row))))
;; query profile after delete ;; query profile after delete
(let [params {::th/type :profile (let [params {::th/type :profile

View file

@ -6,13 +6,14 @@
(ns backend-tests.rpc-team-test (ns backend-tests.rpc-team-test
(:require (:require
[backend-tests.helpers :as th]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.db :as db] [app.db :as db]
[app.http :as http] [app.http :as http]
[app.rpc :as-alias rpc]
[app.storage :as sto] [app.storage :as sto]
[app.tokens :as tokens] [app.tokens :as tokens]
[app.util.time :as dt] [app.util.time :as dt]
[backend-tests.helpers :as th]
[clojure.test :as t] [clojure.test :as t]
[datoteka.core :as fs] [datoteka.core :as fs]
[mockery.core :refer [with-mocks]])) [mockery.core :refer [with-mocks]]))
@ -65,7 +66,7 @@
;; get invitation token ;; get invitation token
(let [params {::th/type :get-team-invitation-token (let [params {::th/type :get-team-invitation-token
:profile-id (:id profile1) ::rpc/profile-id (:id profile1)
:team-id (:id team) :team-id (:id team)
:email "foo@bar.com"} :email "foo@bar.com"}
out (th/command! params)] out (th/command! params)]
@ -214,7 +215,7 @@
:role "editor" :role "editor"
:valid-until (dt/in-future "48h")}) :valid-until (dt/in-future "48h")})
(let [data {::th/type :verify-token :token token :profile-id (:id profile2)} (let [data {::th/type :verify-token :token token ::rpc/profile-id (:id profile2)}
out (th/command! data)] out (th/command! data)]
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (th/success? out)) (t/is (th/success? out))
@ -235,7 +236,7 @@
:role "editor" :role "editor"
:valid-until (dt/in-future "48h")}) :valid-until (dt/in-future "48h")})
(let [data {::th/type :verify-token :token token :profile-id (:id profile1)} (let [data {::th/type :verify-token :token token ::rpc/profile-id (:id profile1)}
out (th/command! data)] out (th/command! data)]
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (not (th/success? out))) (t/is (not (th/success? out)))

View file

@ -100,6 +100,7 @@
out (th/query! data)] out (th/query! data)]
;; (th/print-result! out) ;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)] (let [result (:result out)]
(t/is (contains? result :file)) (t/is (contains? result :file))
(t/is (contains? result :project))))) (t/is (contains? result :project)))))

View file

@ -10,6 +10,7 @@
[app.db :as db] [app.db :as db]
[app.http :as http] [app.http :as http]
[app.storage :as sto] [app.storage :as sto]
[app.rpc :as-alias rpc]
[backend-tests.helpers :as th] [backend-tests.helpers :as th]
[clojure.test :as t] [clojure.test :as t]
[mockery.core :refer [with-mocks]])) [mockery.core :refer [with-mocks]]))
@ -28,7 +29,7 @@
(t/testing "create webhook" (t/testing "create webhook"
(let [params {::th/type :create-webhook (let [params {::th/type :create-webhook
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:team-id team-id :team-id team-id
:uri "http://example.com" :uri "http://example.com"
:mtype "application/json"} :mtype "application/json"}
@ -54,7 +55,7 @@
(t/testing "update webhook 1 (success)" (t/testing "update webhook 1 (success)"
(let [params {::th/type :update-webhook (let [params {::th/type :update-webhook
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:id (:id @whook) :id (:id @whook)
:uri (:uri @whook) :uri (:uri @whook)
:mtype "application/transit+json" :mtype "application/transit+json"
@ -82,7 +83,7 @@
(t/testing "update webhook 2 (change uri)" (t/testing "update webhook 2 (change uri)"
(let [params {::th/type :update-webhook (let [params {::th/type :update-webhook
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:id (:id @whook) :id (:id @whook)
:uri (str (:uri @whook) "/test") :uri (str (:uri @whook) "/test")
:mtype "application/transit+json" :mtype "application/transit+json"
@ -97,7 +98,7 @@
(t/testing "update webhook 3 (not authorized)" (t/testing "update webhook 3 (not authorized)"
(let [params {::th/type :update-webhook (let [params {::th/type :update-webhook
:profile-id uuid/zero ::rpc/profile-id uuid/zero
:id (:id @whook) :id (:id @whook)
:uri (str (:uri @whook) "/test") :uri (str (:uri @whook) "/test")
:mtype "application/transit+json" :mtype "application/transit+json"
@ -115,7 +116,7 @@
(t/testing "delete webhook (success)" (t/testing "delete webhook (success)"
(let [params {::th/type :delete-webhook (let [params {::th/type :delete-webhook
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:id (:id @whook)} :id (:id @whook)}
out (th/command! params)] out (th/command! params)]
@ -128,7 +129,7 @@
(t/testing "delete webhook (unauthorozed)" (t/testing "delete webhook (unauthorozed)"
(let [params {::th/type :delete-webhook (let [params {::th/type :delete-webhook
:profile-id uuid/zero ::rpc/profile-id uuid/zero
:id (:id @whook)} :id (:id @whook)}
out (th/command! params)] out (th/command! params)]
@ -149,7 +150,7 @@
(let [prof (th/create-profile* 1 {:is-active true}) (let [prof (th/create-profile* 1 {:is-active true})
team-id (:default-team-id prof) team-id (:default-team-id prof)
params {::th/type :create-webhook params {::th/type :create-webhook
:profile-id (:id prof) ::rpc/profile-id (:id prof)
:team-id team-id :team-id team-id
:uri "http://example.com" :uri "http://example.com"
:mtype "application/json"} :mtype "application/json"}

View file

@ -113,6 +113,10 @@ http {
proxy_pass http://127.0.0.1:6060/api; proxy_pass http://127.0.0.1:6060/api;
} }
location /admin {
proxy_pass http://127.0.0.1:6063/admin;
}
location /webhooks { location /webhooks {
proxy_pass http://127.0.0.1:6060/webhooks; proxy_pass http://127.0.0.1:6060/webhooks;
} }

View file

@ -25,7 +25,7 @@ http {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
error_log /dev/stdout; error_log /dev/stderr;
access_log /dev/stdout; access_log /dev/stdout;
gzip on; gzip on;
@ -60,29 +60,6 @@ http {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
etag off; etag off;
root /var/www/app/;
location ~* \.(js|css).*$ {
add_header Cache-Control "max-age=86400" always; # 24 hours
}
location ~* \.(html).*$ {
add_header Cache-Control "no-cache, max-age=0" always;
}
location /api/export {
proxy_pass http://penpot-exporter:6061;
}
location /api {
proxy_pass http://penpot-backend:6060/api;
}
location /ws/notifications {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_pass http://penpot-backend:6060/ws/notifications;
}
location @handle_redirect { location @handle_redirect {
set $redirect_uri "$upstream_http_location"; set $redirect_uri "$upstream_http_location";
@ -116,5 +93,35 @@ http {
alias /opt/data/assets; alias /opt/data/assets;
add_header x-internal-redirect "$upstream_http_x_accel_redirect"; add_header x-internal-redirect "$upstream_http_x_accel_redirect";
} }
location /api/export {
proxy_pass http://penpot-exporter:6061;
}
location /api {
proxy_pass http://penpot-backend:6060/api;
}
location /admin {
proxy_pass http://penpot-admin:6063/admin;
}
location /ws/notifications {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_pass http://penpot-backend:6060/ws/notifications;
}
location / {
location ~* \.(js|css).*$ {
add_header Cache-Control "max-age=86400" always; # 24 hours
}
location ~* \.(html).*$ {
add_header Cache-Control "no-cache, max-age=0" always;
}
root /var/www/app/;
}
} }
} }

View file

@ -102,6 +102,10 @@
(= :profile-blocked (:code cause))) (= :profile-blocked (:code cause)))
(reset! error (tr "errors.profile-blocked")) (reset! error (tr "errors.profile-blocked"))
(and (= :restriction (:type cause))
(= :admin-only-profile (:code cause)))
(reset! error (tr "errors.profile-blocked"))
(and (= :validation (:type cause)) (and (= :validation (:type cause))
(= :wrong-credentials (:code cause))) (= :wrong-credentials (:code cause)))
(reset! error (tr "errors.wrong-credentials")) (reset! error (tr "errors.wrong-credentials"))