From 35b69f4b9b937218dd42b3ae0d5e7d0c92d2b06e Mon Sep 17 00:00:00 2001 From: Milton Reder Date: Thu, 9 Nov 2023 11:27:23 -0500 Subject: [PATCH] SQL-215 doc scanning --- .../pedestal/interceptor/xapi/document.cljc | 38 ++++++++++++ .../pedestal/interceptor/xapi/statements.cljc | 9 +-- .../com/yetanalytics/lrs/pedestal/routes.cljc | 60 +++++++++++-------- 3 files changed, 78 insertions(+), 29 deletions(-) create mode 100644 src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/document.cljc diff --git a/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/document.cljc b/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/document.cljc new file mode 100644 index 00000000..ed6ffbcc --- /dev/null +++ b/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/document.cljc @@ -0,0 +1,38 @@ +(ns com.yetanalytics.lrs.pedestal.interceptor.xapi.document + "Document Interceptors" + (:require + [io.pedestal.interceptor.chain :as chain] + #?(:clj [clojure.java.io :as io])) + #?(:clj (:import [java.io ByteArrayOutputStream ByteArrayInputStream]))) + +#?(:clj + (defn stream->bytes + [input-stream] + (let [baos (ByteArrayOutputStream.)] + (io/copy input-stream baos) + (.toByteArray baos)))) + +(defn scan-document + "Return an interceptor that will scan document request bodies given a + file-scanner fn. Reads body into a byte array (in clojure)" + [file-scanner] + {:name ::scan-document + :enter + (fn [ctx] + (let [body-bytes (-> ctx + (get-in [:request :body]) + #?(:clj stream->bytes :cljs identity))] + (if-let [scan-error (file-scanner + #?(:clj (ByteArrayInputStream. body-bytes) + :cljs body-bytes))] + (assoc (chain/terminate ctx) + :response + {:status 400 + :body {:error + {:message + (format "Document scan failed, Error: %s" + (:message scan-error))}}}) + (assoc-in ctx + [:request :body] + #?(:clj (ByteArrayInputStream. body-bytes) + :cljs body-bytes)))))}) diff --git a/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/statements.cljc b/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/statements.cljc index 893ac17d..1e3e57f2 100644 --- a/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/statements.cljc +++ b/src/main/com/yetanalytics/lrs/pedestal/interceptor/xapi/statements.cljc @@ -252,10 +252,11 @@ (assoc (chain/terminate ctx) :response {:status 400 - :body {:error {:message - (format "Scan failed, Errors: %s" - (cs/join ", " - (map :message attachment-errors)))}}})) + :body {:error + {:message + (format "Attachment scan failed, Errors: %s" + (cs/join ", " + (map :message attachment-errors)))}}})) ctx)))}) (def set-consistent-through diff --git a/src/main/com/yetanalytics/lrs/pedestal/routes.cljc b/src/main/com/yetanalytics/lrs/pedestal/routes.cljc index 4055eb40..a1b1815e 100644 --- a/src/main/com/yetanalytics/lrs/pedestal/routes.cljc +++ b/src/main/com/yetanalytics/lrs/pedestal/routes.cljc @@ -2,6 +2,7 @@ (:require [com.yetanalytics.lrs.pedestal.interceptor :as i] [com.yetanalytics.lrs.pedestal.interceptor.xapi :as xapi-i] + [com.yetanalytics.lrs.pedestal.interceptor.xapi.document :as doc-i] [com.yetanalytics.lrs.pedestal.interceptor.xapi.statements :as statements-i] [com.yetanalytics.lrs.pedestal.routes.about :as about] [com.yetanalytics.lrs.pedestal.routes.statements :as statements] @@ -17,7 +18,9 @@ {:status 405}) (defn build-document-routes - [interceptors & {:keys [path-prefix] :or {path-prefix "/xapi"}}] + [interceptors & {:keys [path-prefix + file-scanner] + :or {path-prefix "/xapi"}}] ;; Build all possible doc routes by looping over each pair of ;; resource + doc type and HTTP method (into @@ -39,29 +42,35 @@ method-not-allowed :route-name (keyword route-name-ns (name method))] (let [params-interceptors - [(xapi-i/params-interceptor - (case resource-tuple - ["activities" "state"] - (case method - :put :xapi.activities.state.PUT.request/params - :post :xapi.activities.state.POST.request/params - :get :xapi.activities.state.GET.request/params - :head :xapi.activities.state.GET.request/params - :delete :xapi.activities.state.DELETE.request/params) - ["activities" "profile"] - (case method - :put :xapi.activities.profile.PUT.request/params - :post :xapi.activities.profile.POST.request/params - :get :xapi.activities.profile.GET.request/params - :head :xapi.activities.profile.GET.request/params - :delete :xapi.activities.profile.DELETE.request/params) - ["agents" "profile"] - (case method - :put :xapi.agents.profile.PUT.request/params - :post :xapi.agents.profile.POST.request/params - :get :xapi.agents.profile.GET.request/params - :head :xapi.agents.profile.GET.request/params - :delete :xapi.agents.profile.DELETE.request/params)))] + (cond-> + [(xapi-i/params-interceptor + (case resource-tuple + ["activities" "state"] + (case method + :put :xapi.activities.state.PUT.request/params + :post :xapi.activities.state.POST.request/params + :get :xapi.activities.state.GET.request/params + :head :xapi.activities.state.GET.request/params + :delete :xapi.activities.state.DELETE.request/params) + ["activities" "profile"] + (case method + :put :xapi.activities.profile.PUT.request/params + :post :xapi.activities.profile.POST.request/params + :get :xapi.activities.profile.GET.request/params + :head :xapi.activities.profile.GET.request/params + :delete :xapi.activities.profile.DELETE.request/params) + ["agents" "profile"] + (case method + :put :xapi.agents.profile.PUT.request/params + :post :xapi.agents.profile.POST.request/params + :get :xapi.agents.profile.GET.request/params + :head :xapi.agents.profile.GET.request/params + :delete :xapi.agents.profile.DELETE.request/params)))] + ;; Scan files if scanner is present on PUT/POST + (and file-scanner + (contains? #{:put :post} method)) + (conj + (doc-i/scan-document file-scanner))) method-interceptor (case method :put documents/handle-put @@ -211,4 +220,5 @@ ;; documents (build-document-routes document-interceptors - :path-prefix path-prefix)))) + :path-prefix path-prefix + :file-scanner file-scanner))))