mirror of
https://github.com/penpot/penpot.git
synced 2025-06-01 10:51:37 +02:00
✨ Add mvp access-token support
This commit is contained in:
parent
b90aef4e1d
commit
890583a13a
23 changed files with 907 additions and 100 deletions
|
@ -26,12 +26,18 @@
|
|||
(when token
|
||||
(tokens/verify props {:token token :iss "access-token"})))
|
||||
|
||||
(defn- get-token-perms
|
||||
(def sql:get-token-data
|
||||
"SELECT perms, profile_id, expires_at
|
||||
FROM access_token
|
||||
WHERE id = ?
|
||||
AND (expires_at IS NULL
|
||||
OR (expires_at > now()));")
|
||||
|
||||
(defn- get-token-data
|
||||
[pool token-id]
|
||||
(when-not (db/read-only? pool)
|
||||
(when-let [token (db/get* pool :access-token {:id token-id} {:columns [:perms]})]
|
||||
(some-> (:perms token)
|
||||
(db/decode-pgarray #{})))))
|
||||
(some-> (db/exec-one! pool [sql:get-token-data token-id])
|
||||
(update :perms db/decode-pgarray #{}))))
|
||||
|
||||
(defn- wrap-soft-auth
|
||||
"Soft Authentication, will be executed synchronously on the undertow
|
||||
|
@ -56,10 +62,14 @@
|
|||
"Authorization middleware, will be executed synchronously on vthread."
|
||||
[handler {:keys [::db/pool]}]
|
||||
(fn [request]
|
||||
(let [perms (some->> (::id request) (get-token-perms pool))]
|
||||
(let [{:keys [perms profile-id expires-at]} (some->> (::id request) (get-token-data pool))]
|
||||
(handler (cond-> request
|
||||
(some? perms)
|
||||
(assoc ::perms perms))))))
|
||||
(assoc ::perms perms)
|
||||
(some? profile-id)
|
||||
(assoc ::profile-id profile-id)
|
||||
(some? expires-at)
|
||||
(assoc ::expires-at expires-at))))))
|
||||
|
||||
(def soft-auth
|
||||
{:name ::soft-auth
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.http :as-alias http]
|
||||
[app.http.access-token :as-alias actoken]
|
||||
[app.http.client :as http.client]
|
||||
[app.loggers.audit.tasks :as-alias tasks]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
|
@ -152,7 +153,11 @@
|
|||
(dissoc :profile-id)
|
||||
(dissoc :type)))
|
||||
|
||||
(clean-props))]
|
||||
(clean-props))
|
||||
|
||||
token-id (::actoken/id request)
|
||||
context (d/without-nils
|
||||
{:access-token-id (some-> token-id str)})]
|
||||
|
||||
{::type (or (::type resultm)
|
||||
(::rpc/type cfg))
|
||||
|
@ -161,6 +166,7 @@
|
|||
::profile-id profile-id
|
||||
::ip-addr (some-> request parse-client-ip)
|
||||
::props props
|
||||
::context context
|
||||
|
||||
;; NOTE: for batch-key lookup we need the params as-is
|
||||
;; because the rpc api does not need to know the
|
||||
|
@ -188,6 +194,7 @@
|
|||
:type (::type event)
|
||||
:profile-id (::profile-id event)
|
||||
:ip-addr (::ip-addr event)
|
||||
:context (::context event)
|
||||
:props (::props event)}]
|
||||
|
||||
(when (contains? cf/flags :audit-log)
|
||||
|
@ -201,6 +208,7 @@
|
|||
(db/insert! conn-or-pool :audit-log
|
||||
(-> params
|
||||
(update :props db/tjson)
|
||||
(update :context db/tjson)
|
||||
(update :ip-addr db/inet)
|
||||
(assoc :created-at now)
|
||||
(assoc :tracked-at now)
|
||||
|
|
|
@ -315,7 +315,8 @@
|
|||
{:name "0101-mod-server-error-report-table"
|
||||
:fn (mg/resource "app/migrations/sql/0101-mod-server-error-report-table.sql")}
|
||||
|
||||
])
|
||||
{:name "0102-mod-access-token-table"
|
||||
:fn (mg/resource "app/migrations/sql/0102-mod-access-token-table.sql")}])
|
||||
|
||||
(defn apply-migrations!
|
||||
[pool name migrations]
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE access_token
|
||||
ADD COLUMN expires_at timestamptz NULL;
|
|
@ -112,22 +112,6 @@
|
|||
:hint "authentication required for this endpoint")
|
||||
(f cfg params)))))
|
||||
|
||||
(defn- wrap-access-token
|
||||
"Wraps service method with access token validation."
|
||||
[_ f {:keys [::sv/name] :as mdata}]
|
||||
(if (contains? cf/flags :access-tokens)
|
||||
(fn [cfg params]
|
||||
(let [request (::http/request params)]
|
||||
(if (contains? request ::actoken/id)
|
||||
(let [perms (::actoken/perms request #{})]
|
||||
(if (contains? perms name)
|
||||
(f cfg params)
|
||||
(ex/raise :type :authorization
|
||||
:code :operation-not-allowed
|
||||
:allowed perms)))
|
||||
(f cfg params))))
|
||||
f))
|
||||
|
||||
(defn- wrap-audit
|
||||
[_ f mdata]
|
||||
(if (or (contains? cf/flags :webhooks)
|
||||
|
@ -157,8 +141,7 @@
|
|||
(rlimit/wrap cfg $ mdata)
|
||||
(wrap-audit cfg $ mdata)
|
||||
(wrap-spec-conform cfg $ mdata)
|
||||
(wrap-authentication cfg $ mdata)
|
||||
(wrap-access-token cfg $ mdata)))
|
||||
(wrap-authentication cfg $ mdata)))
|
||||
|
||||
(defn- wrap
|
||||
[cfg f mdata]
|
||||
|
|
|
@ -19,18 +19,19 @@
|
|||
[clojure.spec.alpha :as s]))
|
||||
|
||||
(defn- decode-row
|
||||
[{:keys [perms] :as row}]
|
||||
(cond-> row
|
||||
(db/pgarray? perms "text")
|
||||
(assoc :perms (db/decode-pgarray perms #{}))))
|
||||
[row]
|
||||
(dissoc row :perms))
|
||||
|
||||
(defn- create-access-token
|
||||
[{:keys [::conn ::main/props]} profile-id name perms]
|
||||
(defn create-access-token
|
||||
[{:keys [::db/conn ::main/props]} profile-id name expiration]
|
||||
(let [created-at (dt/now)
|
||||
token-id (uuid/next)
|
||||
token (tokens/generate props {:iss "access-token"
|
||||
:tid token-id
|
||||
:iat created-at})]
|
||||
:iat created-at})
|
||||
|
||||
expires-at (some-> expiration dt/in-future)]
|
||||
|
||||
(db/insert! conn :access-token
|
||||
{:id token-id
|
||||
:name name
|
||||
|
@ -38,33 +39,36 @@
|
|||
:profile-id profile-id
|
||||
:created-at created-at
|
||||
:updated-at created-at
|
||||
:perms (db/create-array conn "text" perms)})))
|
||||
:expires-at expires-at
|
||||
:perms (db/create-array conn "text" [])})))
|
||||
|
||||
|
||||
(defn repl-create-access-token
|
||||
[{:keys [::db/pool] :as system} profile-id name perms]
|
||||
[{:keys [::db/pool] :as system} profile-id name expiration]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [props (:app.setup/props system)]
|
||||
(create-access-token {::conn conn ::main/props props}
|
||||
(create-access-token {::db/conn conn ::main/props props}
|
||||
profile-id
|
||||
name
|
||||
perms))))
|
||||
expiration))))
|
||||
|
||||
(s/def ::name ::us/not-empty-string)
|
||||
(s/def ::perms ::us/set-of-strings)
|
||||
(s/def ::expiration ::dt/duration)
|
||||
|
||||
(s/def ::create-access-token
|
||||
(s/keys :req [::rpc/profile-id]
|
||||
:req-un [::name ::perms]))
|
||||
:req-un [::name]
|
||||
:opt-un [::expiration]))
|
||||
|
||||
(sv/defmethod ::create-access-token
|
||||
{::doc/added "1.18"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id name perms]}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id name expiration]}]
|
||||
(db/with-atomic [conn pool]
|
||||
(let [cfg (assoc cfg ::conn conn)]
|
||||
(let [cfg (assoc cfg ::db/conn conn)]
|
||||
(quotes/check-quote! conn
|
||||
{::quotes/id ::quotes/access-tokens-per-profile
|
||||
::quotes/profile-id profile-id})
|
||||
(-> (create-access-token cfg profile-id name perms)
|
||||
(-> (create-access-token cfg profile-id name expiration)
|
||||
(decode-row)))))
|
||||
|
||||
(s/def ::delete-access-token
|
||||
|
@ -83,5 +87,8 @@
|
|||
(sv/defmethod ::get-access-tokens
|
||||
{::doc/added "1.18"}
|
||||
[{:keys [::db/pool]} {:keys [::rpc/profile-id]}]
|
||||
(->> (db/query pool :access-token {:profile-id profile-id})
|
||||
(->> (db/query pool :access-token
|
||||
{:profile-id profile-id}
|
||||
{:order-by [[:expires-at :asc] [:created-at :asc]]
|
||||
:columns [:id :name :perms :created-at :updated-at :expires-at]})
|
||||
(mapv decode-row)))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue