diff --git a/code/src/sixsq/nuvla/auth/password.clj b/code/src/sixsq/nuvla/auth/password.clj index ab734c71d..013aad7c8 100644 --- a/code/src/sixsq/nuvla/auth/password.clj +++ b/code/src/sixsq/nuvla/auth/password.clj @@ -7,6 +7,7 @@ [sixsq.nuvla.db.filter.parser :as parser] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-context :as ec] [sixsq.nuvla.server.resources.user-identifier :as user-identifier])) @@ -74,6 +75,9 @@ :credential-password credential-id->credential :hash)] + (ec/add-to-context :visible-to [(:id user)]) + (ec/add-linked-identifier (:id user)) + (ec/add-linked-identifier (:credential-password user)) (when (valid-password? password password-hash) user))) diff --git a/code/src/sixsq/nuvla/server/app/routes.clj b/code/src/sixsq/nuvla/server/app/routes.clj index bfc0ae30b..de363e479 100644 --- a/code/src/sixsq/nuvla/server/app/routes.clj +++ b/code/src/sixsq/nuvla/server/app/routes.clj @@ -6,18 +6,24 @@ [sixsq.nuvla.server.app.params :as p] [sixsq.nuvla.server.resources.common.crud :as crud] [sixsq.nuvla.server.resources.common.dynamic-load :as dyn] + [sixsq.nuvla.server.resources.common.event-context :as ec] [sixsq.nuvla.server.util.response :as r])) (def collection-routes (let-routes [uri (str p/service-context ":resource-name")] (POST uri request + (ec/add-to-context :params (:params request)) + (ec/add-to-context :category "add") (crud/add request)) (PUT uri request + (ec/add-to-context :params (:params request)) (crud/query request)) (GET uri request + (ec/add-to-context :params (:params request)) (crud/query request)) (DELETE uri request + (ec/add-to-context :params (:params request)) (crud/bulk-delete request)) (ANY uri request (throw (r/ex-bad-method request))))) @@ -26,10 +32,15 @@ (def resource-routes (let-routes [uri (str p/service-context ":resource-name/:uuid")] (GET uri request + (ec/add-to-context :params (:params request)) (crud/retrieve request)) (PUT uri request + (ec/add-to-context :params (:params request)) + (ec/add-to-context :category "edit") (crud/edit request)) (DELETE uri request + (ec/add-to-context :params (:params request)) + (ec/add-to-context :category "delete") (crud/delete request)) (ANY uri request (throw (r/ex-bad-method request))))) @@ -38,12 +49,15 @@ (def action-routes (let-routes [uri (str p/service-context ":resource-name/:uuid/:action")] (ANY uri request + (ec/add-to-context :params (:params request)) + (ec/add-to-context :category "action") (crud/do-action request)))) (def bulk-action-routes (let-routes [uri (str p/service-context ":resource-name/:action")] (PATCH uri request + (ec/add-to-context :params (:params request)) (crud/bulk-action request)))) @@ -76,3 +90,4 @@ (route/resources (str p/service-context "static"))] (dyn/resource-routes) final-routes)))) + diff --git a/code/src/sixsq/nuvla/server/app/server.clj b/code/src/sixsq/nuvla/server/app/server.clj index ebb21c479..68fdc35a2 100644 --- a/code/src/sixsq/nuvla/server/app/server.clj +++ b/code/src/sixsq/nuvla/server/app/server.clj @@ -17,6 +17,7 @@ [sixsq.nuvla.server.middleware.cimi-params :refer [wrap-cimi-params]] [sixsq.nuvla.server.middleware.default-content-type :refer [default-content-type]] [sixsq.nuvla.server.middleware.exception-handler :refer [wrap-exceptions]] + [sixsq.nuvla.server.middleware.eventer :refer [wrap-eventer]] [sixsq.nuvla.server.middleware.gzip :refer [wrap-gzip-uncompress]] [sixsq.nuvla.server.middleware.logger :refer [wrap-logger]] [sixsq.nuvla.server.middleware.redirect-cep :refer [redirect-cep]] @@ -46,9 +47,10 @@ wrap-nested-params wrap-params wrap-exceptions - wrap-authn-info (wrap-json-body {:keywords? true}) wrap-gzip-uncompress + wrap-eventer + wrap-authn-info (wrap-json-response {:pretty true :escape-non-ascii true}) (default-content-type "application/json") diff --git a/code/src/sixsq/nuvla/server/middleware/eventer.clj b/code/src/sixsq/nuvla/server/middleware/eventer.clj new file mode 100644 index 000000000..852870c48 --- /dev/null +++ b/code/src/sixsq/nuvla/server/middleware/eventer.clj @@ -0,0 +1,17 @@ +(ns sixsq.nuvla.server.middleware.eventer + (:require [sixsq.nuvla.server.resources.common.event-config :as config] + [sixsq.nuvla.server.resources.common.event-context :as ec] + [sixsq.nuvla.server.resources.event.utils :as eu])) + +(defn wrap-eventer + "Creates an event after handler execution, if eventing is enabled for the resource type." + [handler] + (fn [request] + (ec/with-context + (let [response (handler request) + resource-type (-> (ec/get-context) :params :resource-name)] + (when (config/events-enabled? resource-type) + (let [event (eu/build-event (ec/get-context) request response)] + (when (config/log-event? event response) + (eu/add-event event)))) + response)))) diff --git a/code/src/sixsq/nuvla/server/resources/common/crud.clj b/code/src/sixsq/nuvla/server/resources/common/crud.clj index 3c8079e32..9c1cefbca 100644 --- a/code/src/sixsq/nuvla/server/resources/common/crud.clj +++ b/code/src/sixsq/nuvla/server/resources/common/crud.clj @@ -92,6 +92,16 @@ [resource-id] (retrieve-by-id resource-id {:nuvla/authn auth/internal-identity})) +(defn retrieve-by-id-as-admin1 + "Same as `retrieve-by-id-as-admin` but if the resource is not found returns nil + instead of throwing an exception." + [resource-id] + (try (retrieve-by-id-as-admin resource-id) + (catch Exception ex + (when-not (= 404 (:status (ex-data ex))) + (throw ex))))) + + (defn id->user-request [id request] {:params (u/id->request-params id) diff --git a/code/src/sixsq/nuvla/server/resources/common/event_config.clj b/code/src/sixsq/nuvla/server/resources/common/event_config.clj new file mode 100644 index 000000000..e8fba5470 --- /dev/null +++ b/code/src/sixsq/nuvla/server/resources/common/event_config.clj @@ -0,0 +1,82 @@ +(ns sixsq.nuvla.server.resources.common.event-config + (:require [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.utils :as u])) + +;; +;; Dispatch functions +;; + +(defn resource-type-dispatch [resource-type] + resource-type) + + +(defn event-name-dispatch [{event-name :name :as _event} & rest] + event-name) + + +;; +;; Enabled/disabled events +;; + +(defmulti events-enabled? + "Returns true if events should be logged for the given resource-type, false otherwise." + resource-type-dispatch) + + +(defmethod events-enabled? :default + [_resource-type] + false) + + +;; +;; Whitelist and blacklist event types per resource type +;; + +(defmulti log-event? + "Returns true if the event should be logged, false otherwise." + event-name-dispatch) + + +(defmethod log-event? :default + [{event-name :name :as _event} {:keys [status] :as _response}] + (and (not= 405 status) + (some? event-name))) + + +;; +;; Event human readable description +;; + +(defmulti event-description + "Returns a human-readable description of the event" + event-name-dispatch) + + +(defmethod event-description :default + [{:keys [success authn-info category content] event-name :name :as _event} + & [{:keys [resource] :as _context}]] + (if success + (let [user-name-or-id (or (some-> authn-info :user-id crud/retrieve-by-id-as-admin1 :name) + (:user-id authn-info)) + resource-id (-> content :resource :href) + resource-type (u/id->resource-type resource-id) + resource (or resource + (crud/retrieve-by-id-as-admin1 resource-id)) + resource-name (:name resource) + resource-name-or-id (or resource-name resource-id)] + (case category + ("add" "edit" "delete" "action") + (str (or user-name-or-id "An anonymous user") + (case category + "add" (str " added " resource-type " " resource-name-or-id) + "edit" (str " edited " resource-type " " resource-name-or-id) + "delete" (str " deleted " resource-type " " resource-name-or-id) + "action" (let [action (some->> event-name (re-matches #".*\.(.*)") second)] + (str " executed action " action " on " resource-type " " resource-name-or-id)) + nil) + ".") + ("state" "alarm" "email" "user") + event-name ;; FIXME: improve description in this case + event-name)) + (str event-name " attempt failed."))) + diff --git a/code/src/sixsq/nuvla/server/resources/common/event_context.clj b/code/src/sixsq/nuvla/server/resources/common/event_context.clj new file mode 100644 index 000000000..a90c2176b --- /dev/null +++ b/code/src/sixsq/nuvla/server/resources/common/event_context.clj @@ -0,0 +1,40 @@ +(ns sixsq.nuvla.server.resources.common.event-context + (:require [sixsq.nuvla.server.util.time :as t])) + + +(def ^:dynamic *context*) + + +(defmacro with-context + "Initializes an event context map. + Information can be added to the context via `add-to-context`. + The context can be retrieved via `get-context`." + [& body] + `(binding [*context* (atom {:timestamp (t/now-str)})] + ~@body)) + + +(defn get-context + "Returns the current context." + [] + (some-> *context* deref)) + + +(defn add-to-context + "Adds value `v` to the current context under key `k`." + [k v] + (when @*context* + (swap! *context* assoc k v))) + +(defn add-to-visible-to + "Adds `v` to visible to" + [& claims] + (when @*context* + (swap! *context* update :visible-to concat claims))) + + +(defn add-linked-identifier + "Adds the identifier of a linked entity to the context." + [id] + (when (and (some? id) @*context*) + (swap! *context* update :linked-identifiers conj id))) diff --git a/code/src/sixsq/nuvla/server/resources/common/utils.clj b/code/src/sixsq/nuvla/server/resources/common/utils.clj index f085ae4ea..b5bf9b4fe 100644 --- a/code/src/sixsq/nuvla/server/resources/common/utils.clj +++ b/code/src/sixsq/nuvla/server/resources/common/utils.clj @@ -67,6 +67,11 @@ (str resource-name "/" (random-uuid))) +(defn resource-id + [resource-type uuid] + (str resource-type "/" uuid)) + + (defn parse-id "Parses a resource id to provide a tuple of [resource-type uuid] for an id. For ids that don't have an identifier part (e.g. the cloud-entry-point), the @@ -86,6 +91,25 @@ uuid (assoc :uuid uuid)))) +(defn request->resource-type + "Extracts the resource type from a request map." + [request] + (get-in request [:params :resource-name])) + + +(defn request->resource-uuid + "Extracts the resource uuid from a request map." + [request] + (get-in request [:params :uuid])) + + +(defn request->resource-id + "Extracts the resource id from a request map." + [request] + (some->> (request->resource-uuid request) + (resource-id (request->resource-type request)))) + + (defn id->resource-type "Parses a resource id to provide a tuple of [resource-type uuid] for an id. For ids that don't have an identifier part (e.g. the cloud-entry-point), the diff --git a/code/src/sixsq/nuvla/server/resources/credential.clj b/code/src/sixsq/nuvla/server/resources/credential.clj index cacd63a8c..fbd031c81 100644 --- a/code/src/sixsq/nuvla/server/resources/credential.clj +++ b/code/src/sixsq/nuvla/server/resources/credential.clj @@ -9,6 +9,8 @@ passwords) or other services (e.g. TLS credentials for Docker). Creating new [sixsq.nuvla.auth.utils :as auth] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.event-context :as ectx] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.job :as job] @@ -314,13 +316,31 @@ passwords) or other services (e.g. TLS credentials for Docker). Creating new (defmethod crud/delete resource-type [{{uuid :uuid} :params :as request}] - (-> (str resource-type "/" uuid) - (db/retrieve request) - (a/throw-cannot-delete request) - (special-delete request))) + (let [resource (-> (str resource-type "/" uuid) + (db/retrieve request)) + delete-resp (-> resource + (a/throw-cannot-delete request) + (special-delete request))] + (ectx/add-to-context :resource resource) + (ectx/add-to-context :acl (:acl resource)) + delete-resp)) (def query-impl (std-crud/query-fn resource-type collection-acl collection-type)) (defmethod crud/query resource-type [request] (query-impl request)) + + +;; +;; Events +;; + +(defmethod ec/events-enabled? resource-type + [_resource-type] + true) + + +(defmethod ec/log-event? "credential.check" + [_event _response] + false) diff --git a/code/src/sixsq/nuvla/server/resources/deployment.clj b/code/src/sixsq/nuvla/server/resources/deployment.clj index d25f92993..168b2f044 100644 --- a/code/src/sixsq/nuvla/server/resources/deployment.clj +++ b/code/src/sixsq/nuvla/server/resources/deployment.clj @@ -9,10 +9,11 @@ a container orchestration engine. [sixsq.nuvla.auth.utils :as auth] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.event-context :as ectx] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.deployment.utils :as utils] - [sixsq.nuvla.server.resources.event.utils :as event-utils] [sixsq.nuvla.server.resources.job.interface :as job-interface] [sixsq.nuvla.server.resources.module.utils :as module-utils] [sixsq.nuvla.server.resources.resource-metadata :as md] @@ -136,8 +137,7 @@ a container orchestration engine. (defn create-deployment [{:keys [parent deployment-set] :as deployment} {:keys [base-uri] :as request}] (some-> parent (crud/get-resource-throw-nok request)) - (let [authn-info (auth/current-authentication request) - deployment-set-name (some-> deployment-set + (let [deployment-set-name (some-> deployment-set crud/retrieve-by-id-as-admin :name) ;; FIXME: Correct the value passed to the python API. @@ -148,13 +148,13 @@ a container orchestration engine. :owner (auth/current-active-claim request)) (cond-> deployment-set-name (assoc :deployment-set-name deployment-set-name)) (utils/throw-when-payment-required request)) - create-response (add-impl (assoc request :body deployment)) + create-response (add-impl (assoc request :body deployment))] - deployment-id (get-in create-response [:body :resource-id]) - - msg (get-in create-response [:body :message])] - - (event-utils/create-event deployment-id msg (a/default-acl authn-info)) + ;; legacy event logging + #_(let [authn-info (auth/current-authentication request) + deployment-id (get-in create-response [:body :resource-id]) + msg (get-in create-response [:body :message])] + (event-utils/create-event deployment-id msg (a/default-acl authn-info) :category "state")) create-response)) @@ -244,6 +244,8 @@ a container orchestration engine. delete-response (-> deployment (a/throw-cannot-delete request) (db/delete request))] + (ectx/add-to-context :acl (:acl deployment)) + (ectx/add-to-context :resource deployment) (utils/delete-all-child-resources deployment-id) delete-response) (catch Exception e @@ -386,9 +388,11 @@ a container orchestration engine. [request] (try (a/throw-cannot-add collection-acl request) - (-> (crud/get-resource-throw-nok request) - (select-keys [:module :data :name :description :tags]) - (create-deployment request)) + (let [response (-> (crud/get-resource-throw-nok request) + (select-keys [:module :data :name :description :tags]) + (create-deployment request))] + (ectx/add-linked-identifier (get-in response [:body :resource-id])) + response) (catch Exception e (or (ex-data e) (throw e))))) @@ -536,6 +540,55 @@ a container orchestration engine. :body dep-updated :nuvla/authn authn-info})))) + +;; +;; Events +;; + +(defmethod ec/events-enabled? resource-type + [_resource-type] + true) + + +(defmethod ec/log-event? "deployment.create-log" + [_event _response] + false) + + +(defmethod ec/log-event? "deployment.check-dct" + [_event _response] + false) + + +(defmethod ec/log-event? "deployment.fetch-module" + [_event _response] + false) + + +(defmethod ec/event-description "deployment.start" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " started deployment.")) + "Deployment start attempt failed.")) + + +(defmethod ec/event-description "deployment.stop" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " stopped deployment.")) + "Deployment stop attempt failed.")) + + +(defmethod ec/event-description "deployment.clone" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " cloned deployment.")) + "Deployment clone attempt failed.")) + + ;; ;; initialization ;; diff --git a/code/src/sixsq/nuvla/server/resources/deployment/utils.clj b/code/src/sixsq/nuvla/server/resources/deployment/utils.clj index 06f2b4284..114a091fe 100644 --- a/code/src/sixsq/nuvla/server/resources/deployment/utils.clj +++ b/code/src/sixsq/nuvla/server/resources/deployment/utils.clj @@ -9,11 +9,11 @@ [sixsq.nuvla.pricing.payment :as payment] [sixsq.nuvla.server.middleware.cimi-params.impl :as cimi-params-impl] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-context :as ec] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.configuration-nuvla :as config-nuvla] [sixsq.nuvla.server.resources.credential :as credential] [sixsq.nuvla.server.resources.credential-template-api-key :as cred-api-key] - [sixsq.nuvla.server.resources.event.utils :as event-utils] [sixsq.nuvla.server.resources.job :as job] [sixsq.nuvla.server.resources.job.interface :as job-interface] [sixsq.nuvla.server.resources.resource-log :as resource-log] @@ -120,7 +120,11 @@ (when (not= job-status 201) (throw (r/ex-response (format "unable to create async job to %s deployment" action) 500 id))) - (event-utils/create-event id job-msg (a/default-acl (auth/current-authentication request))) + (ec/add-linked-identifier job-id) + + ;; Legacy event logging + #_(event-utils/create-event id job-msg (a/default-acl (auth/current-authentication request)) :category "state") + (r/map-response job-msg 202 id job-id))) diff --git a/code/src/sixsq/nuvla/server/resources/event/utils.clj b/code/src/sixsq/nuvla/server/resources/event/utils.clj index c4fb85526..24c19c975 100644 --- a/code/src/sixsq/nuvla/server/resources/event/utils.clj +++ b/code/src/sixsq/nuvla/server/resources/event/utils.clj @@ -4,40 +4,210 @@ [sixsq.nuvla.auth.utils :as auth] [sixsq.nuvla.db.filter.parser :as parser] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.event :as event] + [sixsq.nuvla.server.util.time :as t] [sixsq.nuvla.server.util.time :as time])) +(defn request-event-name + "Returns a string of the form ." + [{{:keys [resource-name uuid action]} :params :as _context} + {:keys [request-method] :as _request}] + (if uuid + (if action + (some->> action (str resource-name ".")) + (case request-method + :put (str resource-name ".edit") + :delete (str resource-name ".delete") + nil)) + (case request-method + :post (str resource-name ".add") + :delete (str resource-name ".bulk.delete") + :patch (some->> action (str resource-name ".bulk.")) + nil))) + + +(defn get-success + [{:keys [status] :as _response}] + (<= 200 status 399)) + + +(defn get-event-name + [{:keys [event-name] :as context} request] + (or event-name + (request-event-name context request))) + + +(defn get-category + [{:keys [category] :as _context}] + (or category "action")) + + +(defn get-timestamp + [{:keys [timestamp] :as _context}] + (or timestamp (t/now-str))) + + +(defn retrieve-by-id + [id] + (try + (:body (crud/retrieve {:params (u/id->request-params id) + :request-method :get + :nuvla/authn auth/internal-identity})) + (catch Exception _ex + nil))) + + +(defn get-resource-href + [{{:keys [resource-name uuid]} :params :as _context} response] + (or (some->> uuid (str resource-name "/")) + (-> response :body :resource-id))) + + +(defn transform-acl + [acl] + (when acl + {:owners (vec (concat (:edit-data acl) (:owners acl)))})) + +(defn derive-acl-from-resource + [context response] + (when-let [acl (some-> (get-resource-href context response) + retrieve-by-id + :acl)] + (transform-acl acl))) + + +(defn get-acl + [{:keys [visible-to acl] :as context} response] + (let [visible-to (remove nil? visible-to)] + (or (when (seq visible-to) + {:owners (-> visible-to (conj "group/nuvla-admin") distinct vec)}) + (transform-acl acl) + (derive-acl-from-resource context response) + {:owners ["group/nuvla-admin"]}))) + + +(defn get-severity + [{:keys [severity] :as _context}] + (or severity "medium")) + + +(defn get-resource + [context response] + {:href (get-resource-href context response)}) + + +(defn get-linked-identifiers + [{:keys [linked-identifiers] :or {linked-identifiers []} :as _context}] + linked-identifiers) + + +(defn get-linked-resource-ids + [{{:keys [linked-identifiers]} :content :as _event} resource-type] + (->> linked-identifiers + (filter (comp #(= resource-type %) u/id->resource-type)))) + + +(defn get-linked-resources + ([{{:keys [linked-identifiers]} :content :as _event}] + (->> linked-identifiers + (keep crud/retrieve-by-id-as-admin1))) + ([{{:keys [linked-identifiers]} :content :as _event} resource-type] + (->> linked-identifiers + (filter (comp #(= resource-type %) u/id->resource-type)) + (keep crud/retrieve-by-id-as-admin1)))) + + +(defn set-description + [event context] + (let [event-description (ec/event-description event context)] + (cond-> event + event-description (assoc :description event-description)))) + + +(defn build-event + [context request response] + (-> {:resource-type event/resource-type + :name (get-event-name context request) + :success (get-success response) + :category (get-category context) + :timestamp (get-timestamp context) + :authn-info (auth/current-authentication request) + :acl (get-acl context response) + :severity (get-severity context) + :content {:resource (get-resource context response) + :linked-identifiers (get-linked-identifiers context)}} + (set-description context))) + + +(defn add-event + [event] + (let [create-request {:params {:resource-name event/resource-type} + :body event + :nuvla/authn auth/internal-identity}] + (crud/add create-request))) + + (def topic event/resource-type) +;; FIXME: duplicated (defn create-event [resource-href state acl & {:keys [severity category timestamp] - :or {severity "medium" - category "action"}}] - (let [event-map {:resource-type event/resource-type + :or {severity "medium" + category "action"}}] + (let [event-map {:name "legacy" + :success true + :resource-type event/resource-type :content {:resource {:href resource-href} :state state} :severity severity :category category :timestamp (or timestamp (time/now-str)) - :acl acl} + :acl acl + :authn-info {}} create-request {:params {:resource-name event/resource-type} :body event-map :nuvla/authn auth/internal-identity}] (crud/add create-request))) +(defn query-events + ([resource-href opts] + (query-events (assoc opts :resource-href resource-href))) + ([{:keys [resource-href linked-identifier category state start end orderby last] event-name :name :as opts}] + (some-> event/resource-type + (crud/query-as-admin + {:cimi-params + (cond-> + {:filter (parser/parse-cimi-filter + (str/join " and " + (cond-> [] + resource-href (conj (str "content/resource/href='" resource-href "'")) + (and (contains? opts :resource-href) (nil? resource-href)) (conj (str "content/resource/href=null")) + event-name (conj (str "name='" event-name "'")) + category (conj (str "category='" category "'")) + state (conj (str "content/state='" state "'")) + linked-identifier (conj (str "content/linked-identifiers='" linked-identifier "'")) + start (conj (str "timestamp>='" start "'")) + end (conj (str "timestamp<'" end "'")))))} + orderby (assoc :orderby orderby) + last (assoc :last last))}) + second))) + +;; FIXME: duplicated (defn search-event [resource-href {:keys [category state start end]}] (some-> event/resource-type (crud/query-as-admin {:cimi-params - {:filter (parser/parse-cimi-filter - (str/join " and " + {:filter (parser/parse-cimi-filter + (str/join " and " (cond-> [(str "content/resource/href='" resource-href "'")] - category (conj (str "category='" category "'")) - state (conj (str "content/state='" state "'")) - start (conj (str "timestamp>='" start "'")) - end (conj (str "timestamp<'" end "'")))))}}) + category (conj (str "category='" category "'")) + state (conj (str "content/state='" state "'")) + start (conj (str "timestamp>='" start "'")) + end (conj (str "timestamp<'" end "'")))))}}) second)) diff --git a/code/src/sixsq/nuvla/server/resources/infrastructure_service.clj b/code/src/sixsq/nuvla/server/resources/infrastructure_service.clj index 8779eb0e9..6fd96ba91 100644 --- a/code/src/sixsq/nuvla/server/resources/infrastructure_service.clj +++ b/code/src/sixsq/nuvla/server/resources/infrastructure_service.clj @@ -15,9 +15,10 @@ existing `infrastructure-service-template` resource. [sixsq.nuvla.auth.utils :as auth] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.event-context :as ectx] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] [sixsq.nuvla.server.resources.common.utils :as u] - [sixsq.nuvla.server.resources.event.utils :as event-utils] [sixsq.nuvla.server.resources.resource-metadata :as md] [sixsq.nuvla.server.resources.spec.infrastructure-service :as infra-service] [sixsq.nuvla.server.resources.spec.infrastructure-service-template-generic :as infra-srvc-gen] @@ -36,6 +37,41 @@ existing `infrastructure-service-template` resource. (def collection-acl {:query ["group/nuvla-user"] :add ["group/nuvla-user"]}) + +;; +;; Events +;; + + +(defmethod ec/events-enabled? resource-type + [_resource-type] + true) + + +(defmethod ec/event-description "infrastructure-service.start" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " started infrastructure service.")) + "Infrastructure service start attempt failed.")) + + +(defmethod ec/event-description "infrastructure-service.stop" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " stopped infrastructure service.")) + "Infrastructure service stop attempt failed.")) + + +(defmethod ec/event-description "infrastructure-service.terminate" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " terminated infrastructure service.")) + "Infrastructure service terminate attempt failed.")) + + ;; ;; initialization ;; @@ -237,12 +273,13 @@ existing `infrastructure-service-template` resource. (defn event-state-change - [{current-state :state id :id} {{new-state :state} :body :as request}] - (when (and new-state (not (= current-state new-state))) - (event-utils/create-event id new-state - (a/default-acl (auth/current-authentication request)) - :severity "low" - :category "state"))) + [{_current-state :state _id :id} {{_new-state :state} :body :as _request}] + ;; legacy events + #_(when (and new-state (not (= current-state new-state))) + (event-utils/create-event id new-state + (a/default-acl (auth/current-authentication request)) + :severity "low" + :category "state"))) (def edit-impl (std-crud/edit-fn resource-type)) @@ -275,18 +312,21 @@ existing `infrastructure-service-template` resource. (defn post-delete-hooks - [{{uuid :uuid} :params :as request} delete-resp] - (let [id (str resource-type "/" uuid)] - (when (= 200 (:status delete-resp)) - (event-utils/create-event id "DELETED" - (a/default-acl (auth/current-authentication request)) - :severity "low" - :category "state")))) + [{{_uuid :uuid} :params :as _request} _delete-resp] + ;; legacy events + #_(let [id (str resource-type "/" uuid)] + (when (= 200 (:status delete-resp)) + (event-utils/create-event id "DELETED" + (a/default-acl (auth/current-authentication request)) + :severity "low" + :category "state")))) (defmethod crud/delete resource-type [{{uuid :uuid} :params :as request}] (let [resource (db/retrieve (str resource-type "/" uuid) request) delete-resp (delete resource request)] + (ectx/add-to-context :resource resource) + (ectx/add-to-context :acl (:acl resource)) (post-delete-hooks request delete-resp) delete-resp)) diff --git a/code/src/sixsq/nuvla/server/resources/infrastructure_service_coe.clj b/code/src/sixsq/nuvla/server/resources/infrastructure_service_coe.clj index f6fcb83ea..f63f95f4a 100644 --- a/code/src/sixsq/nuvla/server/resources/infrastructure_service_coe.clj +++ b/code/src/sixsq/nuvla/server/resources/infrastructure_service_coe.clj @@ -8,8 +8,8 @@ manage it. [sixsq.nuvla.auth.utils :as auth] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-context :as ec] [sixsq.nuvla.server.resources.common.utils :as u] - [sixsq.nuvla.server.resources.event.utils :as event-utils] [sixsq.nuvla.server.resources.infrastructure-service :as infra-service] [sixsq.nuvla.server.resources.job :as job] [sixsq.nuvla.server.resources.spec.infrastructure-service-coe :as infra-service-coe] @@ -81,8 +81,10 @@ manage it. (if (= 201 status) (let [job-msg (format "created job %s with id %s" job-name job-id)] (edit-infra-service resource request #(assoc % :state new-state)) - (infra-service/event-state-change resource (assoc-in request [:body :state] new-state)) - (event-utils/create-event resource-id job-msg (a/default-acl (auth/current-authentication request))) + (ec/add-linked-identifier job-id) + ;; legacy events + #_(infra-service/event-state-change resource (assoc-in request [:body :state] new-state)) + #_(event-utils/create-event resource-id job-msg (a/default-acl (auth/current-authentication request))) (r/map-response job-msg 202 resource-id job-id)) (throw (r/ex-response (format "unable to create job %s" job-name) 500 resource-id)))) (catch Exception e @@ -180,9 +182,10 @@ manage it. (u/update-timestamps) (u/set-updated-by request) (db/edit request)) - (event-utils/create-event id "STARTING" (a/default-acl (auth/current-authentication request)) - :severity "low" - :category "state") + ;; Legacy events + #_(event-utils/create-event id "STARTING" (a/default-acl (auth/current-authentication request)) + :severity "low" + :category "state") (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))) diff --git a/code/src/sixsq/nuvla/server/resources/infrastructure_service_generic.clj b/code/src/sixsq/nuvla/server/resources/infrastructure_service_generic.clj index d22b2574b..929f9abc5 100644 --- a/code/src/sixsq/nuvla/server/resources/infrastructure_service_generic.clj +++ b/code/src/sixsq/nuvla/server/resources/infrastructure_service_generic.clj @@ -38,8 +38,9 @@ an endpoint. (defmethod infra-service/post-add-hook method - [service request] - (try + [_service _request] + ;; legacy events + #_(try (let [id (:id service) category "state"] (event-utils/create-event id diff --git a/code/src/sixsq/nuvla/server/resources/module.clj b/code/src/sixsq/nuvla/server/resources/module.clj index 13bc9ee0d..d3fd48ba9 100644 --- a/code/src/sixsq/nuvla/server/resources/module.clj +++ b/code/src/sixsq/nuvla/server/resources/module.clj @@ -10,6 +10,8 @@ component, or application. [sixsq.nuvla.db.filter.parser :as parser] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.event-context :as ectx] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.configuration-nuvla :as config-nuvla] @@ -359,11 +361,12 @@ component, or application. (defmethod crud/delete resource-type [request] (try - (-> request - utils/retrieve-module-meta - throw-project-cannot-delete-if-has-children - (a/throw-cannot-edit request) - (delete-all request)) + (let [module-meta (utils/retrieve-module-meta request)] + (ectx/add-to-context :acl (:acl module-meta)) + (-> module-meta + throw-project-cannot-delete-if-has-children + (a/throw-cannot-edit request) + (delete-all request))) (catch Exception e (or (ex-data e) (throw e))))) @@ -481,6 +484,21 @@ component, or application. deploy-op-present? (update :operations conj deploy-op) can-delete? (update :operations conj delete-version-op)))) + +;; +;; Events +;; + +(defmethod ec/events-enabled? resource-type + [_resource-type] + true) + + +(defmethod ec/log-event? "module.validate-docker-compose" + [_event _response] + false) + + ;; ;; initialization ;; diff --git a/code/src/sixsq/nuvla/server/resources/nuvlabox.clj b/code/src/sixsq/nuvla/server/resources/nuvlabox.clj index 37a906d84..ebb83a1bd 100644 --- a/code/src/sixsq/nuvla/server/resources/nuvlabox.clj +++ b/code/src/sixsq/nuvla/server/resources/nuvlabox.clj @@ -13,10 +13,11 @@ particular NuvlaBox release. [sixsq.nuvla.auth.utils.acl :as acl-utils] [sixsq.nuvla.db.impl :as db] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.event-context :as ectx] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.credential.vpn-utils :as vpn-utils] - [sixsq.nuvla.server.resources.event.utils :as event-utils] [sixsq.nuvla.server.resources.job :as job] [sixsq.nuvla.server.resources.job.interface :as job-interface] [sixsq.nuvla.server.resources.nuvlabox.utils :as utils] @@ -331,9 +332,12 @@ particular NuvlaBox release. (defmethod crud/delete resource-type [{{uuid :uuid} :params :as request}] - (let [id (str resource-type "/" uuid)] + (let [id (str resource-type "/" uuid) + nuvlabox (db/retrieve id request)] + (ectx/add-to-context :acl (:acl nuvlabox)) + (ectx/add-to-context :resource nuvlabox) (try - (-> (db/retrieve id request) + (-> nuvlabox (a/throw-cannot-delete request) (u/throw-can-not-do-action utils/can-delete? "delete")) (let [resp (delete-impl request)] @@ -500,7 +504,9 @@ particular NuvlaBox release. (when (not= job-status 201) (throw (r/ex-response "unable to create async job to decommission nuvlabox resources" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))) @@ -542,7 +548,9 @@ particular NuvlaBox release. (when (not= job-status 201) (throw (r/ex-response "unable to create async job to check nuvlabox api" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))) @@ -580,7 +588,9 @@ particular NuvlaBox release. (when (not= job-status 201) (throw (r/ex-response "unable to create async job to reboot nuvlabox" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))) @@ -629,7 +639,9 @@ particular NuvlaBox release. ", with async " job-id)] (when (not= job-status 201) (throw (r/ex-response "unable to create async job to cluster NuvlaBox" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))) @@ -676,7 +688,9 @@ particular NuvlaBox release. (when (not= job-status 201) (throw (r/ex-response "unable to create async job to add SSH key to NuvlaBox" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response (or (:private-key ssh-credential) job-msg) 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))) @@ -745,7 +759,9 @@ particular NuvlaBox release. (when (not= job-status 201) (throw (r/ex-response "unable to create async job to remove SSH key from NuvlaBox" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))))) @@ -797,7 +813,9 @@ particular NuvlaBox release. ", with async " job-id)] (when (not= job-status 201) (throw (r/ex-response "unable to create async job to update NuvlaBox" 500 id))) - (event-utils/create-event id job-msg acl) + (ectx/add-linked-identifier job-id) + ;; Legacy event + ;; (event-utils/create-event id job-msg acl) (r/map-response job-msg 202 id job-id)) (catch Exception e (or (ex-data e) (throw e))))))) @@ -1074,6 +1092,106 @@ particular NuvlaBox release. (utils/can-unsuspend? resource) (conj unsuspend-op) ))))) + +;; +;; Events +;; + +(defmethod ec/events-enabled? resource-type + [_resource-type] + true) + + +(defmethod ec/log-event? "nuvlabox.check-api" + [_event _response] + false) + + +(defmethod ec/log-event? "nuvlabox.assemble-playbooks" + [_event _response] + false) + + +(defmethod ec/event-description "nuvlabox.activate" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " activated nuvlabox.")) + "Nuvlabox activation attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.commission" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " commissioned nuvlabox.")) + "Nuvlabox commissioning attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.decommission" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " decommissioned nuvlabox.")) + "Nuvlabox decommission attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.reboot" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " rebooted nuvlabox.")) + "Nuvlabox reboot attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.add-ssh-key" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " added ssh key to nuvlabox.")) + "Nuvlabox ssh key addition attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.revoke-ssh-key" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " revoked ssh key from nuvlabox.")) + "Nuvlabox commission attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.update-nuvlabox" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " updated nuvlabox.")) + "Nuvlabox update attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.enable-host-level-management" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " enabled host-level management on nuvlabox.")) + "Nuvlabox host-level management enabling attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.disable-host-level-management" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " disabled host-level management on nuvlabox.")) + "Nuvlabox host-level management disabling attempt failed.")) + + +(defmethod ec/event-description "nuvlabox.unsuspend" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " unsuspended nuvlabox.")) + "Nuvlabox unsuspend attempt failed.")) + + ;; ;; initialization ;; diff --git a/code/src/sixsq/nuvla/server/resources/session.clj b/code/src/sixsq/nuvla/server/resources/session.clj index 3c3d6f38e..f2c5c3cc4 100644 --- a/code/src/sixsq/nuvla/server/resources/session.clj +++ b/code/src/sixsq/nuvla/server/resources/session.clj @@ -90,9 +90,12 @@ status, a 'set-cookie' header, and a 'location' header with the created [sixsq.nuvla.server.middleware.authn-info :as authn-info] [sixsq.nuvla.server.resources.common.crud :as crud] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] + [sixsq.nuvla.server.resources.common.event-config :as ec] + [sixsq.nuvla.server.resources.common.event-context :as ectx] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.configuration-nuvla :as config-nuvla] [sixsq.nuvla.server.resources.email :as email] + [sixsq.nuvla.server.resources.event.utils :as eu] [sixsq.nuvla.server.resources.group :as group] [sixsq.nuvla.server.resources.resource-metadata :as md] [sixsq.nuvla.server.resources.spec.session :as session] @@ -302,6 +305,7 @@ status, a 'set-cookie' header, and a 'location' header with the created (defmethod crud/delete resource-type [request] + (ectx/add-to-visible-to (auth/current-user-id request)) (let [response (delete-impl request) cookies (delete-cookie response)] (merge response cookies))) @@ -461,9 +465,16 @@ status, a 'set-cookie' header, and a 'location' header with the created (format "Switch group cannot be done to requested group: %s!" claim) 403)))) +(defn set-event-context + [{{:keys [claim]} :body :as _request}] + (ectx/add-linked-identifier claim) + (ectx/add-to-visible-to claim)) + + (defmethod crud/do-action [resource-type "switch-group"] [{{uuid :uuid} :params :as request}] (try + (set-event-context request) (let [id (str resource-type "/" uuid)] (-> (db/retrieve id request) (a/throw-cannot-edit request) @@ -506,6 +517,54 @@ status, a 'set-cookie' header, and a 'location' header with the created (catch Exception e (or (ex-data e) (throw e))))) +;; +;; Events +;; + +(defmethod ec/events-enabled? resource-type + [_resource-type] + true) + + +(defmethod ec/log-event? "session.get-peers" + [_event _response] + false) + + +(defmethod ec/log-event? "session.get-groups" + [_event _response] + false) + + +(defmethod ec/event-description "session.add" + [{:keys [success] :as event} & _] + (if success + (when-let [user-name-or-credential (or (some-> (eu/get-linked-resources event "user") first :name) + (some-> (eu/get-linked-resource-ids event "user") first) + (some-> (eu/get-linked-resources event "credential") first :id) + (some-> (eu/get-linked-resource-ids event "credential") first))] + (str user-name-or-credential " logged in.")) + "Login attempt failed.")) + + +(defmethod ec/event-description "session.delete" + [{:keys [success] {:keys [user-id]} :authn-info :as _event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " logged out.")) + "Logout attempt failed.")) + + +(defmethod ec/event-description "session.switch-group" + [{:keys [success] {:keys [user-id]} :authn-info {:keys [linked-identifiers]} :content :as event} & _] + (if success + (when-let [user-name (or (some-> user-id crud/retrieve-by-id-as-admin1 :name) user-id)] + (str user-name " switched to group " + (or (some-> (eu/get-linked-resources event) first :name) + (first linked-identifiers)) + ".")) + "Switch group attempt failed.")) + ;; ;; initialization: no schema for this parent resource diff --git a/code/src/sixsq/nuvla/server/resources/session_api_key.clj b/code/src/sixsq/nuvla/server/resources/session_api_key.clj index c9ae785c0..c7fc29a09 100644 --- a/code/src/sixsq/nuvla/server/resources/session_api_key.clj +++ b/code/src/sixsq/nuvla/server/resources/session_api_key.clj @@ -9,6 +9,7 @@ an API key-secret pair. [sixsq.nuvla.auth.utils.timestamp :as ts] [sixsq.nuvla.server.middleware.authn-info :as authn-info] [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-context :as ec] [sixsq.nuvla.server.resources.common.std-crud :as std-crud] [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.credential-template-api-key :as api-key-tpl] @@ -77,6 +78,7 @@ an API key-secret pair. (defmethod p/tpl->session authn-method [{:keys [href key secret] :as _resource} {:keys [headers] :as _request}] + (ec/add-linked-identifier (uuid->id key)) (let [{{:keys [identity roles]} :claims :as api-key} (retrieve-credential-by-id key)] (if (valid-api-key? api-key secret) (let [session (sutils/create-session identity identity {:href href} headers authn-method) diff --git a/code/src/sixsq/nuvla/server/resources/spec/common.cljc b/code/src/sixsq/nuvla/server/resources/spec/common.cljc index 60d1ba529..ab20479db 100644 --- a/code/src/sixsq/nuvla/server/resources/spec/common.cljc +++ b/code/src/sixsq/nuvla/server/resources/spec/common.cljc @@ -14,14 +14,6 @@ :json-schema/description "reference to another resource"))) -(s/def ::resource-link - (-> (st/spec (s/keys :req-un [::href])) - (assoc :name "resource-link" - :json-schema/type "map" - :json-schema/display-name "resource link" - :json-schema/description "map containing a reference (href) to a resource"))) - - ;; ;; core meta ;; diff --git a/code/src/sixsq/nuvla/server/resources/spec/event.cljc b/code/src/sixsq/nuvla/server/resources/spec/event.cljc index 0349114d3..79e23fb28 100644 --- a/code/src/sixsq/nuvla/server/resources/spec/event.cljc +++ b/code/src/sixsq/nuvla/server/resources/spec/event.cljc @@ -1,19 +1,20 @@ (ns sixsq.nuvla.server.resources.spec.event (:require + [clojure.spec.alpha :as s] [clojure.spec.alpha :as s] [sixsq.nuvla.server.resources.spec.common :as common] [sixsq.nuvla.server.resources.spec.core :as core] + [sixsq.nuvla.server.resources.spec.session :as session] [sixsq.nuvla.server.util.spec :as su] [spec-tools.core :as st])) (s/def ::category - (-> (st/spec #{"state" "alarm" "action" "system" "user" "email"}) + (-> (st/spec #{"add" "edit" "delete" "action" "state" "alarm" "email" "user"}) (assoc :name "category" :json-schema/type "string" :json-schema/description "category of event" - :json-schema/value-scope {:values ["state" "alarm" "action" "system" "user" - "email"]} + :json-schema/value-scope {:values ["add" "edit" "delete" "action" "state" "alarm" "email" "user"]} :json-schema/order 30))) @@ -46,7 +47,7 @@ ;; Events may need to reference resources that do not follow the CIMI. ;; conventions. Allow for a more flexible schema to be used here. (s/def ::href - (-> (st/spec (s/and string? #(re-matches #"^[a-zA-Z0-9]+[a-zA-Z0-9_./-]*$" %))) + (-> (st/spec (s/nilable ::core/nonblank-string)) (assoc :name "href" :json-schema/type "string" :json-schema/description "reference to associated resource"))) @@ -59,8 +60,24 @@ :json-schema/description "link to associated resource"))) +(s/def ::identifier + (-> (st/spec string?) + (assoc :name "identifier" + :json-schema/type "string" + :json-schema/description "identifier"))) + + +(s/def ::linked-identifiers + (-> (st/spec (s/coll-of ::identifier)) + (assoc :name "linked-identifiers" + :json-schema/type "array" + :json-schema/description "list of linked identifiers"))) + + (s/def ::content - (-> (st/spec (su/only-keys :req-un [::resource ::state])) + (-> (st/spec (su/only-keys :req-un [::resource] + :opt-un [::linked-identifiers + ::state])) (assoc :name "content" :json-schema/type "map" :json-schema/description "content describing event" @@ -68,9 +85,43 @@ :json-schema/order 33))) +(s/def ::user-id + (-> (st/spec ::core/nonblank-string) + (assoc :name "user-id" + :json-schema/type "string" + :json-schema/description "user id"))) + + +(s/def ::claims + (-> (st/spec (s/coll-of ::core/nonblank-string)) + (assoc :name "claims" + :json-schema/type "array" + :json-schema/description "claims"))) + + +(s/def ::authn-info + (-> (st/spec (su/only-keys :opt-un [::user-id ::session/active-claim ::claims])) + (assoc :name "authn-info" + :json-schema/type "map" + :json-schema/description "authentication info" + + :json-schema/order 34))) + + +(s/def ::success + (-> (st/spec boolean?) + (assoc :name "success" + :json-schema/type "boolean" + :json-schema/description "success" + + :json-schema/order 35))) + + (s/def ::schema (su/only-keys-maps common/common-attrs {:req-un [::timestamp ::content ::category - ::severity]})) + ::severity + ::authn-info + ::success]})) diff --git a/code/test/sixsq/nuvla/server/middleware/eventer_test.clj b/code/test/sixsq/nuvla/server/middleware/eventer_test.clj new file mode 100644 index 000000000..80594c488 --- /dev/null +++ b/code/test/sixsq/nuvla/server/middleware/eventer_test.clj @@ -0,0 +1,27 @@ +(ns sixsq.nuvla.server.middleware.eventer-test + (:require + [clojure.test :refer [deftest is use-fixtures]] + [peridot.core :refer [content-type request session]] + [sixsq.nuvla.server.app.params :as p] + [sixsq.nuvla.server.resources.lifecycle-test-utils :as ltu])) + + +(use-fixtures :each ltu/with-test-server-fixture) + + +(def test-url (str p/service-context "session")) + + +(deftest event-wrapper + (let [session-anon (-> (ltu/ring-app) + session + (content-type "application/json")) + event-added (atom false)] + + (with-redefs [sixsq.nuvla.server.resources.event.utils/add-event + (fn [_event] (reset! event-added true))] + (-> session-anon + (request test-url :request-method :post) + (ltu/is-status 200)) + (is (true? @event-added))))) + diff --git a/code/test/sixsq/nuvla/server/resources/common/event_config_test.clj b/code/test/sixsq/nuvla/server/resources/common/event_config_test.clj new file mode 100644 index 000000000..719dd41cd --- /dev/null +++ b/code/test/sixsq/nuvla/server/resources/common/event_config_test.clj @@ -0,0 +1,53 @@ +(ns sixsq.nuvla.server.resources.common.event-config-test + (:require [clojure.test :refer [deftest is]] + [sixsq.nuvla.server.resources.common.crud :as crud] + [sixsq.nuvla.server.resources.common.event-config :as t])) + + +(def logged-event {:name "resource.add" + :category "add" + :success true + :authn-info {:user-id "user/12345"} + :content {:resource {:href "resource/12345"}}}) + + +(def not-logged-event {}) + + +(def disabled-event {:name "resource.validate"}) + + +(def anon-event {:name "resource.add" + :category "add" + :success true + :authn-info {:claims ["group/nuvla-anon"]} + :content {:resource {:href "resource/12345"}}}) + + +(def failure-event {:name "resource.add" + :success false}) + + + +(defmethod t/log-event? + "resource.validate" + [_event _response] + false) + + +(deftest log-event + (is (true? (t/log-event? logged-event {}))) + (is (false? (t/log-event? not-logged-event {}))) + (is (false? (t/log-event? disabled-event {}))) + (is (false? (t/log-event? logged-event {:status 405})))) + + +(deftest event-description + (with-redefs [crud/retrieve-by-id-as-admin + #(case % + "user/12345" {:name "TestUser"} + "resource/12345" {:resource-type "resource" + :name "TestResource"})] + (is (= "TestUser added resource TestResource." (t/event-description logged-event))) + (is (= "An anonymous user added resource TestResource." (t/event-description anon-event))) + (is (= "resource.add attempt failed." (t/event-description failure-event))))) diff --git a/code/test/sixsq/nuvla/server/resources/common/event_context_test.clj b/code/test/sixsq/nuvla/server/resources/common/event_context_test.clj new file mode 100644 index 000000000..4492eb9a6 --- /dev/null +++ b/code/test/sixsq/nuvla/server/resources/common/event_context_test.clj @@ -0,0 +1,22 @@ +(ns sixsq.nuvla.server.resources.common.event-context-test + (:require [clojure.test :refer [deftest is]] + [sixsq.nuvla.server.resources.common.event-context :as t])) + + +(deftest event-context + (let [k :test-key + info "something" + linked-id "linked-identifier"] + (t/with-context + (is (= [:timestamp] (keys (t/get-context)))) + (t/add-to-context k info) + (is (= info (get (t/get-context) k))) + + (t/add-linked-identifier linked-id) + (is (some #{linked-id} (:linked-identifiers (t/get-context)))) + + (t/add-to-visible-to "user/toto") + (is (= #{"user/toto"} (set (:visible-to (t/get-context))))) + (t/add-to-visible-to "user/tata" "user/titi") + (is (= #{"user/tata" "user/titi" "user/toto"} (set (:visible-to (t/get-context))))) + ))) diff --git a/code/test/sixsq/nuvla/server/resources/credential/vpn_utils_test.clj b/code/test/sixsq/nuvla/server/resources/credential/vpn_utils_test.clj index 6dcee3725..82d4c80cd 100644 --- a/code/test/sixsq/nuvla/server/resources/credential/vpn_utils_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential/vpn_utils_test.clj @@ -1,6 +1,7 @@ (ns sixsq.nuvla.server.resources.credential.vpn-utils-test (:require [clojure.data.json :as json] + [clojure.string :as string] [clojure.string :as str] [clojure.test :refer [is]] [peridot.core :refer [content-type header request session]] @@ -76,7 +77,18 @@ :description description-attr :tags tags-attr :template {:href href - :parent infra-service-id}}] + :parent infra-service-id}} + + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + test-claims (-> claims (string/split #"\s")) + authn-info-test {:user-id (first test-claims) + :active-claim (second test-claims) + :claims (set test-claims)} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-test]] @@ -96,13 +108,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-test session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-test ["group/nuvla-admin"] authn-info-test] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -162,6 +186,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str user-id " added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-test + :acl {:owners ["group/nuvla-admin"]}}) + ;; admin should be able to see and delete credential (-> session-admin (request abs-uri) @@ -226,7 +259,15 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))) + (ltu/is-status 200)) + (ltu/is-last-event uri + {:name "credential.delete" + :description (str user-id " deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-test + :acl {:owners ["group/nuvla-admin"]}})) )) )) diff --git a/code/test/sixsq/nuvla/server/resources/credential_api_key_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_api_key_lifecycle_test.clj index f87adc105..3c46afcdf 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_api_key_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_api_key_lifecycle_test.clj @@ -35,7 +35,7 @@ session (content-type "application/json")) session-admin (header session authn-info-header - "group/nuvla-admin group/nuvla-user group/nuvla-anon") + "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") session-user (header session authn-info-header "user/jane user/jane group/nuvla-user group/nuvla-anon") session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") @@ -63,7 +63,17 @@ create-import-href-zero-ttl {:template {:href href :ttl 0}} - create-import-href-no-ttl {:template {:href href}}] + create-import-href-no-ttl {:template {:href href}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}} + admin-group-name "Nuvla Administrator Group"] ;; admin/user query should succeed but be empty (no credentials created yet) (if (env/env :nuvla-super-password) @@ -91,13 +101,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -123,6 +145,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; the secret key must be returned as part of the 201 response (is secret-key) @@ -155,7 +186,16 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})) ;; execute the same tests but now create an API key without an expiry date (let [resp (-> session-user @@ -236,7 +276,8 @@ (request abs-uri) (ltu/body->edn) (ltu/is-status 200) - (ltu/body))] + (ltu/body)) + new-name-attr "UPDATED!"] (is digest) (is (key-utils/valid? secret-key digest)) (is (nil? expiry)) @@ -249,7 +290,7 @@ :request-method :put :body (json/write-str (assoc current - :name "UPDATED!" + :name new-name-attr :claims {:identity "super", :roles ["group/nuvla-user" "group/nuvla-anon" "group/nuvla-admin"]}))) (ltu/body->edn) @@ -266,6 +307,15 @@ (is (= (dissoc expected :updated) (dissoc reread :updated :updated-by))) (is (not= (:updated expected) (:updated reread)))) + (ltu/is-last-event uri + {:name "credential.edit" + :description (str "user/jane edited credential " new-name-attr ".") + :category "edit" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; update the credential by changing the name attribute ;; claims are editable for super (-> session-admin @@ -280,17 +330,27 @@ (ltu/is-status 200)) ;; verify that the attribute has been changed - (let [expected (assoc current :name "UPDATED by super!" - :claims {:identity "super", - :roles ["group/nuvla-user" "group/nuvla-anon" "group/nuvla-admin"]}) - reread (-> session-admin - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body))] + (let [new-name-attr "UPDATED by super!" + expected (assoc current :name new-name-attr + :claims {:identity "super", + :roles ["group/nuvla-user" "group/nuvla-anon" "group/nuvla-admin"]}) + reread (-> session-admin + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body))] (is (= (dissoc expected :updated) (dissoc reread :updated :updated-by))) - (is (not= (:updated expected) (:updated reread))))) + (is (not= (:updated expected) (:updated reread))) + + (ltu/is-last-event uri + {:name "credential.edit" + :description (str admin-group-name " edited credential " new-name-attr ".") + :category "edit" + :success true + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners ["group/nuvla-admin" "user/jane"]}}))) ;; delete the credential (-> session-user diff --git a/code/test/sixsq/nuvla/server/resources/credential_gpg_key_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_gpg_key_lifecycle_test.clj index cc4bce469..3b7c4fd36 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_gpg_key_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_gpg_key_lifecycle_test.clj @@ -46,24 +46,33 @@ (ltu/body)) upload {:template (-> template - ltu/strip-unwanted-attrs - (assoc :href href - :public-key "mypublickey"))} + ltu/strip-unwanted-attrs + (assoc :href href + :public-key "mypublickey"))} upload-pvt-key {:template (-> template - ltu/strip-unwanted-attrs - (assoc :href href - :public-key "mypublickey" - :private-key "******"))} + ltu/strip-unwanted-attrs + (assoc :href href + :public-key "mypublickey" + :private-key "******"))} create-no-href {:template (-> template - ltu/strip-unwanted-attrs - (assoc :public-key "mypublickey"))} + ltu/strip-unwanted-attrs + (assoc :public-key "mypublickey"))} create {:name name-attr :description description-attr :tags tags-attr - :template {:href href}}] + :template {:href href}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; check we can perform search with ordering by name (-> session-admin @@ -90,13 +99,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -124,6 +145,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " uri ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -160,15 +190,15 @@ ;;;;;; ;; upload an existing gpg key and save the private key (let [resp (-> session-user - (request base-uri - :request-method :post - :body (json/write-str upload-pvt-key)) - (ltu/body->edn) - (ltu/is-status 201)) + (request base-uri + :request-method :post + :body (json/write-str upload-pvt-key)) + (ltu/body->edn) + (ltu/is-status 201)) id (ltu/body-resource-id resp) keypair (get-in resp [:response :body]) uri (-> resp - (ltu/location)) + (ltu/location)) abs-uri (str p/service-context uri)] ;; resource id and the uri (location) should be the same @@ -190,9 +220,17 @@ ;; delete the credential (-> session-user - (request abs-uri - :request-method :delete) - (ltu/body->edn) - (ltu/is-status 200))) )) - + (request abs-uri + :request-method :delete) + (ltu/body->edn) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " uri ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_hashed_password_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_hashed_password_lifecycle_test.clj index 8877bd8b6..7f6cecf8d 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_hashed_password_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_hashed_password_lifecycle_test.clj @@ -55,7 +55,16 @@ :description description-attr :tags tags-attr :template {:href href - :password plaintext-password}}] + :password plaintext-password}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -75,13 +84,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -106,6 +127,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -185,6 +215,15 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_amazonec2_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_amazonec2_lifecycle_test.clj index 61722c41d..4bc3eedf4 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_amazonec2_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_amazonec2_lifecycle_test.clj @@ -50,7 +50,16 @@ :tags tags-attr :template {:href href :amazonec2-access-key "abc" - :amazonec2-secret-key "def"}}] + :amazonec2-secret-key "def"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -70,13 +79,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -101,6 +122,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -128,4 +158,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_azure_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_azure_lifecycle_test.clj index 5b03aa27f..bd119a108 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_azure_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_azure_lifecycle_test.clj @@ -51,7 +51,16 @@ :template {:href href :azure-subscription-id "abc" :azure-client-secret "def" - :azure-client-id "ghi"}}] + :azure-client-id "ghi"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; check we can perform search with ordering by name (-> session-admin @@ -78,13 +87,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -109,6 +130,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -137,4 +167,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_exoscale_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_exoscale_lifecycle_test.clj index 9fe8b80c2..58651aa82 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_exoscale_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_exoscale_lifecycle_test.clj @@ -50,7 +50,16 @@ :tags tags-attr :template {:href href :exoscale-api-key "abc" - :exoscale-api-secret-key "def"}}] + :exoscale-api-secret-key "def"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -70,13 +79,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -101,6 +122,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -128,4 +158,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_google_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_google_lifecycle_test.clj index 3751ae23e..39e5285ae 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_google_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_google_lifecycle_test.clj @@ -53,7 +53,16 @@ :google-username "username" :client-id "12345678901.apps.googleusercontent.com" :client-secret "secret" - :refresh-token "refresh token"}}] + :refresh-token "refresh token"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -73,13 +82,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -104,6 +125,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -138,4 +168,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_minio_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_minio_lifecycle_test.clj index 60ad81d56..f406b898f 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_minio_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_minio_lifecycle_test.clj @@ -56,7 +56,16 @@ :template {:href href :parent parent-value :access-key access-key-value - :secret-key secret-key-value}}] + :secret-key secret-key-value}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -76,13 +85,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -107,6 +128,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -137,4 +167,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_openstack_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_openstack_lifecycle_test.clj index 7179c2655..544ed84dd 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_openstack_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_openstack_lifecycle_test.clj @@ -48,9 +48,18 @@ create-import-href {:name name-attr :description description-attr :tags tags-attr - :template {:href href - :openstack-username "foo" - :openstack-password "bar"}}] + :template {:href href + :openstack-username "foo" + :openstack-password "bar"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -70,13 +79,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -101,6 +122,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -114,10 +144,10 @@ (let [{:keys [name description tags openstack-username openstack-password]} (-> session-user - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body))] + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body))] (is (= name name-attr)) (is (= description description-attr)) (is (= tags tags-attr)) @@ -129,4 +159,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_registry_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_registry_lifecycle_test.clj index a7b8b6e3a..b31f009bf 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_registry_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_registry_lifecycle_test.clj @@ -57,7 +57,16 @@ :template {:href href :parent parent-value :username username-value - :password password-value}}] + :password password-value}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -77,13 +86,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -108,6 +129,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -149,4 +179,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_swarm_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_swarm_lifecycle_test.clj index 7f5e1dd7a..20bdc99ff 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_swarm_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_infrastructure_service_swarm_lifecycle_test.clj @@ -60,7 +60,16 @@ :parent parent-value :ca ca-value :cert cert-value - :key key-value}}] + :key key-value}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] (testing "Admin/user query should succeed but be empty (no credentials created yet)" (doseq [session [session-admin session-user]] @@ -80,13 +89,25 @@ (ltu/is-status 403))) (testing "Creating a new credential without reference will fail for all types of users" - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-import-no-href)) (ltu/body->edn) - (ltu/is-status 400)))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}))) (testing "Creating a new credential as anon will fail; expect 400 because href cannot be accessed" (-> session-anon @@ -111,6 +132,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -145,7 +175,7 @@ (ltu/is-status 202))))) (testing "Ensure that create and check credential created 2 cred check jobs" - (let [descr-changed "descr changed" + (let [descr-changed "descr changed" job-url-filter (str job-base-uri "?filter=action='credential_check'&target-resource/href='" id "'")] (-> session-user (request job-url-filter @@ -158,4 +188,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_ssh_key_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_ssh_key_lifecycle_test.clj index ebb4198ae..c6f81c948 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_ssh_key_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_ssh_key_lifecycle_test.clj @@ -46,24 +46,33 @@ (ltu/body)) upload {:template (-> template - ltu/strip-unwanted-attrs - (assoc :href href - :public-key "mypublickey"))} + ltu/strip-unwanted-attrs + (assoc :href href + :public-key "mypublickey"))} upload-pvtkey {:template (-> template - ltu/strip-unwanted-attrs - (assoc :href href - :public-key "mypublickey" - :private-key "******"))} + ltu/strip-unwanted-attrs + (assoc :href href + :public-key "mypublickey" + :private-key "******"))} create-no-href {:template (-> template - ltu/strip-unwanted-attrs - (assoc :public-key "mypublickey"))} + ltu/strip-unwanted-attrs + (assoc :public-key "mypublickey"))} create {:name name-attr :description description-attr :tags tags-attr - :template {:href href}}] + :template {:href href}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; check we can perform search with ordering by name (-> session-admin @@ -90,13 +99,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -124,6 +145,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " uri ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -141,10 +171,10 @@ ;; ensure credential contains correct information (let [{:keys [public-key private-key]} (-> session-user - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body))] + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body))] (is (= "mypublickey" public-key (:public-key keypair))) ; it is a custom user SSH key, and no private key was provided...so none was generated nor stored @@ -160,15 +190,15 @@ ;;;;;; ;; upload an existing ssh key and save the private key (let [resp (-> session-user - (request base-uri - :request-method :post - :body (json/write-str upload-pvtkey)) - (ltu/body->edn) - (ltu/is-status 201)) + (request base-uri + :request-method :post + :body (json/write-str upload-pvtkey)) + (ltu/body->edn) + (ltu/is-status 201)) id (ltu/body-resource-id resp) keypair (get-in resp [:response :body]) uri (-> resp - (ltu/location)) + (ltu/location)) abs-uri (str p/service-context uri)] ;; resource id and the uri (location) should be the same @@ -178,10 +208,10 @@ ;; ensure credential contains correct information (let [{:keys [public-key private-key]} (-> session-user - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body))] + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body))] (is (= "mypublickey" public-key (:public-key keypair))) ; even though no private key is generated, the original one is still returned back in the response @@ -190,22 +220,31 @@ ;; delete the credential (-> session-user - (request abs-uri - :request-method :delete) - (ltu/body->edn) - (ltu/is-status 200))) + (request abs-uri + :request-method :delete) + (ltu/body->edn) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " uri ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})) ;;;; ;; ask Nuvla to generate the keypair from scratch (let [resp (-> session-user - (request base-uri - :request-method :post - :body (json/write-str create)) - (ltu/body->edn) - (ltu/is-status 201)) + (request base-uri + :request-method :post + :body (json/write-str create)) + (ltu/body->edn) + (ltu/is-status 201)) id (ltu/body-resource-id resp) uri (-> resp - (ltu/location)) + (ltu/location)) keypair (get-in resp [:response :body]) abs-uri (str p/service-context uri)] @@ -216,10 +255,10 @@ ;; ensure credential contains correct information (let [{:keys [name description tags public-key private-key]} (-> session-user - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body))] + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body))] (is (= name name-attr)) (is (= description description-attr)) (is (= tags tags-attr)) @@ -231,9 +270,9 @@ ;; delete the credential (-> session-user - (request abs-uri - :request-method :delete) - (ltu/body->edn) - (ltu/is-status 200))))) + (request abs-uri + :request-method :delete) + (ltu/body->edn) + (ltu/is-status 200))))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_swarm_token_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_swarm_token_lifecycle_test.clj index 2df8891fd..5fb388e57 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_swarm_token_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_swarm_token_lifecycle_test.clj @@ -55,7 +55,16 @@ :tags tags-attr :template {:href href :scope "MANAGER" - :token "some-swarm-token"}}] + :token "some-swarm-token"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -75,13 +84,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -106,6 +127,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str "user/jane added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}}) + ;; admin/user should be able to see and delete credential (doseq [session [session-admin session-user]] (-> session @@ -138,6 +168,14 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) - + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str "user/jane deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners ["group/nuvla-admin" "user/jane"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/credential_totp_2fa_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/credential_totp_2fa_lifecycle_test.clj index fd925d52c..f2cf76836 100644 --- a/code/test/sixsq/nuvla/server/resources/credential_totp_2fa_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/credential_totp_2fa_lifecycle_test.clj @@ -51,8 +51,18 @@ create-href {:name name-attr :description description-attr :tags tags-attr - :template {:href href - :secret "some-secret"}}] + :template {:href href + :secret "some-secret"}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}} + admin-group-name "Nuvla Administrator Group"] ;; admin/user query should succeed but be empty (no credentials created yet) (doseq [session [session-admin session-user]] @@ -73,13 +83,25 @@ (ltu/is-status 403)) ;; creating a new credential without reference will fail for all types of users - (doseq [session [session-admin session-user session-anon]] + (doseq [[session event-owners authn-info] + [[session-admin ["group/nuvla-admin"] authn-info-admin] + [session-user ["group/nuvla-admin"] authn-info-jane] + [session-anon ["group/nuvla-admin"] authn-info-anon]]] (-> session (request base-uri :request-method :post :body (json/write-str create-no-href)) (ltu/body->edn) - (ltu/is-status 400))) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "credential.add" + :description "credential.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) ;; creating a new credential as anon will fail; expect 400 because href cannot be accessed (-> session-anon @@ -111,6 +133,15 @@ ;; resource id and the uri (location) should be the same (is (= id uri)) + (ltu/is-last-event uri + {:name "credential.add" + :description (str admin-group-name " added credential " name-attr ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners ["group/nuvla-admin"]}}) + ;; admin should be able to see and delete credential (-> session-admin (request abs-uri) @@ -127,10 +158,10 @@ ;; ensure credential contains correct information (let [{:keys [name description tags secret]} (-> session-admin - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body))] + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body))] (is (= name name-attr)) (is (= description description-attr)) (is (= tags tags-attr)) @@ -141,6 +172,13 @@ (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))) - - + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "credential.delete" + :description (str admin-group-name " deleted credential " name-attr ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners ["group/nuvla-admin"]}})))) diff --git a/code/test/sixsq/nuvla/server/resources/deployment_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/deployment_lifecycle_test.clj index b35ed0fa0..9f785cf11 100644 --- a/code/test/sixsq/nuvla/server/resources/deployment_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/deployment_lifecycle_test.clj @@ -34,15 +34,15 @@ (defn- setup-module [session-owner module-data] - (let [_ (create-parent-projects (:path module-data) session-owner) - module-id (-> session-owner - (request module-base-uri - :request-method :post - :body (json/write-str - module-data)) - (ltu/body->edn) - (ltu/is-status 201) - (ltu/location))] + (let [_ (create-parent-projects (:path module-data) session-owner) + module-id (-> session-owner + (request module-base-uri + :request-method :post + :body (json/write-str + module-data)) + (ltu/body->edn) + (ltu/is-status 201) + (ltu/location))] module-id)) (defn valid-module @@ -113,7 +113,11 @@ ;; setup a module that can be referenced from the deployment module-id (setup-module session-user (valid-module subtype valid-module-content)) valid-deployment {:module {:href module-id}} - invalid-deployment {:module {:href "module/doesnt-exist"}}] + invalid-deployment {:module {:href "module/doesnt-exist"}} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user" session-id]} + event-owners-jane ["group/nuvla-admin" "user/jane"]] ;; admin/user query succeeds but is empty (doseq [session [session-admin session-user]] @@ -160,6 +164,15 @@ deployment-url (str p/service-context deployment-id)] + (ltu/is-last-event deployment-id + {:name "deployment.add" + :description (str "user/jane added deployment " deployment-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners event-owners-jane}}) + ;; admin/user should see one deployment (doseq [session [session-user session-admin]] (-> session @@ -197,6 +210,15 @@ (ltu/is-key-value :owner "user/jane") (ltu/is-key-value :owners :acl ["user/jane" "user/tarzan"])) + (ltu/is-last-event deployment-id + {:name "deployment.edit" + :description (str "user/jane edited deployment " deployment-id ".") + :category "edit" + :success true + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners (conj event-owners-jane deployment-id "user/tarzan")}}) + (testing "user should not be able to change parent credential to something not accessible" (with-redefs [crud/retrieve-by-id-as-admin (fn [id] (if (= id "credential/x") @@ -208,15 +230,35 @@ :request-method :put :body (json/write-str {:parent "credential/x"})) (ltu/body->edn) - (ltu/is-status 403)))) + (ltu/is-status 403)) + + (ltu/is-last-event deployment-id + {:name "deployment.edit" + :description "deployment.edit attempt failed." + :category "edit" + :success false + :linked-identifiers [] + :authn-info authn-info-jane + :acl {:owners (conj event-owners-jane deployment-id "user/tarzan")}}))) ;; attempt to start the deployment and check the start job was created - (let [job-url (-> session-user + (let [job-id (-> session-user (request start-url :request-method :post) (ltu/body->edn) (ltu/is-status 202) - (ltu/location-url))] + (ltu/location)) + job-url (ltu/href->url job-id)] + + (ltu/is-last-event deployment-id + {:name "deployment.start" + :description "user/jane started deployment." + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info-jane + :acl {:owners (conj event-owners-jane deployment-id "user/tarzan")}}) + (-> session-user (request job-url) (ltu/body->edn) @@ -395,12 +437,23 @@ (ltu/is-status 200)) ;; try to stop the deployment and check the stop job was created - (let [job-url (-> session-user + (let [job-id (-> session-user (request stop-url :request-method :post) (ltu/body->edn) (ltu/is-status 202) - (ltu/location-url))] + (ltu/location)) + job-url (ltu/href->url job-id)] + + (ltu/is-last-event deployment-id + {:name "deployment.stop" + :description "user/jane stopped deployment." + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info-jane + :acl {:owners (conj event-owners-jane deployment-id "user/tarzan")}}) + (-> session-user (request job-url :request-method :get) @@ -467,18 +520,37 @@ :acl "user/shared"))) ;; verify user can create another deployment from existing one by using clone action - (let [deployment-url-from-dep (-> session-user + (let [deployment-id-from-dep (-> session-user (request (str deployment-url "/clone") :request-method :post :body (json/write-str {:deployment {:href deployment-id}})) (ltu/body->edn) (ltu/is-status 201) - (ltu/location-url))] + (ltu/location)) + deployment-url-from-dep (ltu/href->url deployment-id-from-dep)] + + (ltu/is-last-event deployment-id + {:name "deployment.clone" + :description "user/jane cloned deployment." + :category "action" + :success true + :linked-identifiers [deployment-id-from-dep] + :authn-info authn-info-jane + :acl {:owners (conj event-owners-jane deployment-id "user/tarzan")}}) + (-> session-user (request deployment-url-from-dep :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))) + (ltu/is-status 200)) + + (ltu/is-last-event deployment-id-from-dep + {:name "deployment.delete" + :description (str "user/jane deleted deployment " deployment-id-from-dep ".") + :category "delete" + :success true + :authn-info authn-info-jane + :acl {:owners event-owners-jane}})) ;; verify that the user can delete the deployment (-> session-user @@ -487,6 +559,14 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event deployment-id + {:name "deployment.delete" + :description (str "user/jane deleted deployment API credential for " deployment-id ".") + :category "delete" + :success true + :authn-info authn-info-jane + :acl {:owners (conj ["group/nuvla-admin"] deployment-id)}}) + ;; verify that the deployment has disappeared (-> session-user (request deployment-url) @@ -690,26 +770,30 @@ (deftest lifecycle-bulk-update-force-delete (binding [config-nuvla/*stripe-api-key* nil] - (let [session-anon (-> (ltu/ring-app) - session - (content-type "application/json")) - session-user (header session-anon authn-info-header - "user/jane user/jane group/nuvla-user group/nuvla-anon") + (let [session-anon (-> (ltu/ring-app) + session + (content-type "application/json")) + session-user (header session-anon authn-info-header + "user/jane user/jane group/nuvla-user group/nuvla-anon") ;; setup a module that can be referenced from the deployment - module-id (setup-module session-user (valid-module "component" valid-component)) + module-id (setup-module session-user (valid-module "component" valid-component)) - valid-deployment {:module {:href module-id}} - deployment-id (-> session-user - (request base-uri - :request-method :post - :body (json/write-str valid-deployment)) - (ltu/body->edn) - (ltu/is-status 201) - (ltu/location)) + valid-deployment {:module {:href module-id}} + deployment-id (-> session-user + (request base-uri + :request-method :post + :body (json/write-str valid-deployment)) + (ltu/body->edn) + (ltu/is-status 201) + (ltu/location)) - deployment-url (str p/service-context deployment-id)] + deployment-url (str p/service-context deployment-id) + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + event-owners-jane ["group/nuvla-admin" "user/jane"]] ;; check deployment creation (-> session-user @@ -816,6 +900,13 @@ (ltu/body->edn) (ltu/is-status 200))) + (ltu/is-last-event deployment-id + {:name "deployment.force-delete" + :category "action" + :success true + :authn-info authn-info-jane + :acl {:owners event-owners-jane}}) + (-> session-user (request (str p/service-context module-id) :request-method :delete) @@ -842,7 +933,7 @@ (request deployment-url :request-method :put :body (json/write-str (cond-> - {:name depl-name} + {:name depl-name} depl-tags (assoc :tags depl-tags)))) (ltu/body->edn) (ltu/is-status 200) @@ -875,16 +966,16 @@ (request endpoint :request-method :patch :body (json/write-str (cond-> {:doc {:tags tags}} - filter (assoc :filter filter)))) + filter (assoc :filter filter)))) (ltu/is-status 200)) (run! - (fn [url] - (let [ne (-> session-owner - (request url) - (ltu/body->edn))] - (testing (:name ne) - (ltu/is-key-value ne :tags (expected-fn (-> ne :response :body)))))) - ne-urls)))) + (fn [url] + (let [ne (-> session-owner + (request url) + (ltu/body->edn))] + (testing (:name ne) + (ltu/is-key-value ne :tags (expected-fn (-> ne :response :body)))))) + ne-urls)))) (def endpoint-set-tags (str base-uri "/" "set-tags")) (def endpoint-add-tags (str base-uri "/" "add-tags")) diff --git a/code/test/sixsq/nuvla/server/resources/event_test.clj b/code/test/sixsq/nuvla/server/resources/event_test.clj index 7337af9b1..35dc6c4d4 100644 --- a/code/test/sixsq/nuvla/server/resources/event_test.clj +++ b/code/test/sixsq/nuvla/server/resources/event_test.clj @@ -16,13 +16,16 @@ (def ^:private nb-events 20) -(def valid-event {:acl {:owners ["user/joe"]} - :created "2015-01-16T08:05:00.00Z" - :updated "2015-01-16T08:05:00.00Z" - :timestamp "2015-01-16T08:05:00.00Z" - :content {:resource {:href "run/45614147-aed1-4a24-889d-6365b0b1f2cd"} - :state "Started"} - :severity "critical"}) +(def valid-event {:acl {:owners ["user/joe"]} + :authn-info {} + :name "legacy" + :success true + :created "2015-01-16T08:05:00.00Z" + :updated "2015-01-16T08:05:00.00Z" + :timestamp "2015-01-16T08:05:00.00Z" + :content {:resource {:href "run/45614147-aed1-4a24-889d-6365b0b1f2cd"} + :state "Started"} + :severity "critical"}) (def valid-events diff --git a/code/test/sixsq/nuvla/server/resources/event_utils_test.clj b/code/test/sixsq/nuvla/server/resources/event_utils_test.clj index c9ae05479..1ed29f70d 100644 --- a/code/test/sixsq/nuvla/server/resources/event_utils_test.clj +++ b/code/test/sixsq/nuvla/server/resources/event_utils_test.clj @@ -1,6 +1,7 @@ (ns sixsq.nuvla.server.resources.event-utils-test (:require - [clojure.test :refer [deftest is use-fixtures]] + [clojure.test :refer [deftest is testing use-fixtures]] + [sixsq.nuvla.server.resources.common.utils :as u] [sixsq.nuvla.server.resources.event.utils :as t] [sixsq.nuvla.server.resources.lifecycle-test-utils :as ltu] [sixsq.nuvla.server.util.time :as time])) @@ -8,8 +9,71 @@ (use-fixtures :each ltu/with-test-server-fixture) + +(defn req + [{:keys [nuvla-authn-info method body]}] + {:request-method method + :params {:resource-name "resource" + :uuid "12345"} + :headers {"nuvla-authn-info" nuvla-authn-info} + :body body}) + + +;; TODO: test getters in sixsq.nuvla.server.resources.event.utils + + +(deftest build-event + (with-redefs [time/now-str (constantly "2023-08-17T07:25:57.259Z")] + (let [context {:category "add" + :params {:resource-name "resource"}} + request (req {:nuvla-authn-info "super super group/nuvla-admin" + :method :post + :body {:k "v"}})] + (testing "success" + (let [uuid (u/random-uuid) + id (str "resource/" uuid) + event (t/build-event context request {:status 201 :body {:resource-id id}})] + (is (= {:name "resource.add" + :category "add" + :description (str "An anonymous user added resource " id ".") + :content {:resource {:href id} + :linked-identifiers []} + :authn-info {} + :success true + :severity "medium" + :resource-type "event" + :acl {:owners ["group/nuvla-admin"]} + :timestamp "2023-08-17T07:25:57.259Z"} + event)))) + (testing "failure" + (let [event (t/build-event context request {:status 400})] + (is (= {:name "resource.add" + :category "add" + :description "resource.add attempt failed." + :content {:resource {:href nil} + :linked-identifiers []} + :authn-info {} + :success false + :severity "medium" + :resource-type "event" + :acl {:owners ["group/nuvla-admin"]} + :timestamp "2023-08-17T07:25:57.259Z"} + event))))))) + + +(deftest add-event + (let [context {:category "action" + :params {:resource-name "resource"}} + request (req {:nuvla-authn-info "super super group/nuvla-admin" + :method :post + :body {:k "v"}}) + event (t/build-event context request {:status 200})] + (let [{:keys [status]} (t/add-event event)] + (is (= 201 status))))) + + (deftest search-event - (doseq [category ["action" "system"] + (doseq [category ["action" "add"] timestamp ["2015-01-16T08:05:00.000Z" "2015-01-17T08:05:00.000Z" (time/now-str)]] (t/create-event "user/1" "hello" {:owners ["group/nuvla-admin"]} :category category @@ -18,6 +82,7 @@ (is (= 0 (count (t/search-event "user/2" {})))) (is (= 3 (count (t/search-event "user/1" {:category "action"})))) (is (= 6 (count (t/search-event "user/1" {:start "2015-01-16T08:05:00.000Z"})))) - (is (= 2 (count (t/search-event "user/1" {:end "2015-01-16T08:06:00.000Z"})))) + (is (= 2 (count (t/search-event "user/1" {:end "2015-01-16T08:06:00.000Z"})))) (is (= 1 (count (t/search-event "user/1" {:category "action" - :start "now/d" :end "now+1d/d"}))))) + :start "now/d" :end "now+1d/d"}))))) + diff --git a/code/test/sixsq/nuvla/server/resources/infrastructure_service_coe_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/infrastructure_service_coe_lifecycle_test.clj index 69ede182d..89e2b0c49 100644 --- a/code/test/sixsq/nuvla/server/resources/infrastructure_service_coe_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/infrastructure_service_coe_lifecycle_test.clj @@ -56,7 +56,7 @@ session (content-type "application/json")) session-admin (header session-anon authn-info-header - "group/nuvla-admin group/nuvla-user group/nuvla-anon") + "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") session-user (header session-anon authn-info-header "user/jane user/jane group/nuvla-user group/nuvla-anon") ;; setup a service-group to act as parent for service @@ -101,7 +101,14 @@ :method infra-service-tpl-coe/method :parent service-group-id :subtype subtype - :management-credential credential-id}}] + :management-credential credential-id}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + admin-group-name "Nuvla Administrator Group"] ;; anon create must fail (-> session-anon @@ -112,7 +119,9 @@ (ltu/is-status 400)) ;; check creation - (doseq [session [session-admin session-user]] + (doseq [[session event-owners authn-info user-name-or-id] + [[session-admin ["group/nuvla-admin"] authn-info-admin admin-group-name] + [session-user ["group/nuvla-admin" "user/jane"] authn-info-jane "user/jane"]]] (let [uri (-> session (request base-uri :request-method :post @@ -141,12 +150,30 @@ (is (= "STARTING" (:state service))) (is (= credential-id (:management-credential service)))) + (ltu/is-last-event uri + {:name "infrastructure-service.add" + :description (str user-name-or-id " added infrastructure-service " service-name ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + ;; can NOT delete resource in STARTING state (-> session (request abs-uri :request-method :delete) (ltu/body->edn) (ltu/is-status 409)) + (ltu/is-last-event uri + {:name "infrastructure-service.delete" + :description "infrastructure-service.delete attempt failed." + :category "delete" + :success false + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + ;; set TERMINATED state (set-state-on-is abs-uri session "TERMINATED") @@ -154,7 +181,16 @@ (-> session (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200))))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "infrastructure-service.delete" + :description (str user-name-or-id " deleted infrastructure-service " service-name ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})))))) ;; Validate right CRUD operations and actions are available on resource in @@ -218,22 +254,27 @@ (ltu/is-status 201) (ltu/location)) abs-uri (str p/service-context uri) + event-owners ["group/nuvla-admin" "user/jane"] + authn-info {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} check-event (fn [exp-state] - (let [filter (format "category='state' and content/resource/href='%s' and content/state='%s'" uri exp-state) - state (-> session-user - (content-type "application/x-www-form-urlencoded") - (request "/api/event" - :request-method :put - :body (rc/form-encode {:filter filter})) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/is-count 1) - (ltu/body) - :resources - first - :content - :state)] - (is (= state exp-state))))] + ;; legacy events + #_(let [filter (format "category='state' and content/resource/href='%s' and content/state='%s'" uri exp-state) + state (-> session-user + (content-type "application/x-www-form-urlencoded") + (request "/api/event" + :request-method :put + :body (rc/form-encode {:filter filter})) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/is-count 1) + (ltu/body) + :resources + first + :content + :state)] + (is (= state exp-state))))] ;; STARTING: edit (let [response (-> session-user @@ -271,15 +312,25 @@ (request abs-uri) (ltu/body->edn) (ltu/is-status 200) - (ltu/get-op-url "stop"))] - (-> session-user - (request op-uri - :request-method :post) - (ltu/is-status 202) - (ltu/body->edn))) + (ltu/get-op-url "stop")) + job-id (-> session-user + (request op-uri + :request-method :post) + (ltu/is-status 202) + (ltu/body->edn) + (ltu/location))] + + ;; check event for STOPPING was created + (check-event "STOPPING") - ;; check event for STOPPING was created - (check-event "STOPPING") + (ltu/is-last-event uri + {:name "infrastructure-service.stop" + :description (str "user/jane stopped infrastructure service.") + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info + :acl {:owners event-owners}})) ;; STOPPING: edit (let [response (-> session-user @@ -314,15 +365,25 @@ (request abs-uri) (ltu/body->edn) (ltu/is-status 200) - (ltu/get-op-url "terminate"))] - (-> session-user - (request op-uri - :request-method :post) - (ltu/is-status 202) - (ltu/body->edn))) - - ;; check event for TERMINATING was created - (check-event "TERMINATING") + (ltu/get-op-url "terminate")) + job-id (-> session-user + (request op-uri + :request-method :post) + (ltu/is-status 202) + (ltu/body->edn) + (ltu/location))] + + ;; check event for TERMINATING was created + (check-event "TERMINATING") + + (ltu/is-last-event uri + {:name "infrastructure-service.terminate" + :description (str "user/jane terminated infrastructure service.") + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info + :acl {:owners event-owners}})) ;; TERMINATING: edit (let [response (-> session-user diff --git a/code/test/sixsq/nuvla/server/resources/infrastructure_service_generic_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/infrastructure_service_generic_lifecycle_test.clj index d0f939ae3..a71f24039 100644 --- a/code/test/sixsq/nuvla/server/resources/infrastructure_service_generic_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/infrastructure_service_generic_lifecycle_test.clj @@ -70,7 +70,14 @@ :acl valid-acl :template (merge {:href (str infra-service-tpl/resource-type "/" infra-service-tpl-generic/method)} - valid-service)}] + valid-service)} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + admin-group-name "Nuvla Administrator Group"] ;; admin query succeeds but is empty (-> session-admin @@ -107,7 +114,9 @@ (ltu/is-status 400)) ;; check creation - (doseq [session [session-admin session-user]] + (doseq [[session event-owners authn-info user-name-or-id] + [[session-admin ["group/nuvla-admin" "user/jane"] authn-info-admin admin-group-name] + [session-user ["group/nuvla-admin" "user/jane"] authn-info-jane "user/jane"]]] (let [uri (-> session (request base-uri :request-method :post @@ -136,8 +145,26 @@ (is (:endpoint service)) (is (= "STARTED" (:state service)))) + (ltu/is-last-event uri + {:name "infrastructure-service.add" + :description (str user-name-or-id " added infrastructure-service " service-name ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + ;; can delete resource (-> session (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200)))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "infrastructure-service.delete" + :description (str user-name-or-id " deleted infrastructure-service " service-name ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}))))) diff --git a/code/test/sixsq/nuvla/server/resources/infrastructure_service_vpn_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/infrastructure_service_vpn_lifecycle_test.clj index e5e074e9c..49e4a6df7 100644 --- a/code/test/sixsq/nuvla/server/resources/infrastructure_service_vpn_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/infrastructure_service_vpn_lifecycle_test.clj @@ -40,7 +40,7 @@ session (content-type "application/json")) session-admin (header session-anon authn-info-header - "group/nuvla-admin group/nuvla-user group/nuvla-anon") + "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") session-user (header session-anon authn-info-header "user/jane user/jane group/nuvla-user group/nuvla-anon") ;; setup a service-group to act as parent for service @@ -65,7 +65,14 @@ :tags service-tags :template {:href (str infra-service-tpl/resource-type "/" infra-service-tpl-vpn/method) - :parent service-group-id}}] + :parent service-group-id}} + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]} + admin-group-name "Nuvla Administrator Group"] ;; anon create must fail (-> session-anon @@ -76,7 +83,9 @@ (ltu/is-status 400)) ;; check creation - (doseq [session [session-admin session-user]] + (doseq [[session event-owners authn-info user-name-or-id] + [[session-admin ["group/nuvla-admin"] authn-info-admin admin-group-name] + [session-user ["group/nuvla-admin" "user/jane"] authn-info-jane "user/jane"]]] (let [uri (-> session (request base-uri :request-method :post @@ -101,8 +110,27 @@ (is (:subtype service)) (is (nil? (:endpoint service)))) + (ltu/is-last-event uri + {:name "infrastructure-service.add" + :description (str user-name-or-id " added infrastructure-service " service-name ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + ;; can delete resource (-> session (request abs-uri :request-method :delete) (ltu/body->edn) - (ltu/is-status 200)))))) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "infrastructure-service.delete" + :description (str user-name-or-id " deleted infrastructure-service " service-name ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}))))) + diff --git a/code/test/sixsq/nuvla/server/resources/lifecycle_test_utils.clj b/code/test/sixsq/nuvla/server/resources/lifecycle_test_utils.clj index c6f59e828..96ec3cdb3 100644 --- a/code/test/sixsq/nuvla/server/resources/lifecycle_test_utils.clj +++ b/code/test/sixsq/nuvla/server/resources/lifecycle_test_utils.clj @@ -25,8 +25,10 @@ [sixsq.nuvla.server.middleware.base-uri :refer [wrap-base-uri]] [sixsq.nuvla.server.middleware.cimi-params :refer [wrap-cimi-params]] [sixsq.nuvla.server.middleware.exception-handler :refer [wrap-exceptions]] + [sixsq.nuvla.server.middleware.eventer :refer [wrap-eventer]] [sixsq.nuvla.server.middleware.logger :refer [wrap-logger]] [sixsq.nuvla.server.resources.common.dynamic-load :as dyn] + [sixsq.nuvla.server.resources.event.utils :as event-utils] [sixsq.nuvla.server.util.kafka :as ka] [sixsq.nuvla.server.util.zookeeper :as uzk] [zookeeper :as zk]) @@ -308,7 +310,7 @@ [client server]))) -(def ^:private zk-client-server-cache (atom nil)) +(defonce ^:private zk-client-server-cache (atom nil)) (defn set-zk-client-server-cache @@ -387,7 +389,7 @@ [node client sniffer])) -(def ^:private es-node-client-cache (atom nil)) +(defonce ^:private es-node-client-cache (atom nil)) (defn es-node [] @@ -469,14 +471,15 @@ wrap-nested-params wrap-params wrap-base-uri - wrap-authn-info wrap-exceptions (wrap-json-body {:keywords? true}) + wrap-eventer + wrap-authn-info (wrap-json-response {:pretty true :escape-non-ascii true}) wrap-logger)) -(def ^:private ring-app-cache (atom nil)) +(defonce ^:private ring-app-cache (atom nil)) (defn set-ring-app-cache "Sets the value of the cached ring application. If the current value is nil, @@ -538,7 +541,7 @@ (let [ts (System/currentTimeMillis)] (.close kafka) (log/debug (str "--->: close kafka done in: " - (- (System/currentTimeMillis) ts))) ) + (- (System/currentTimeMillis) ts)))) (ke/delete-dir log-dir)))))) @@ -599,4 +602,73 @@ :body (json/write-str {:dummy "value"})) (is-status 405))))) +;; +;; ACL +;; + +(defmacro is-acl + [expected-acl actual-acl] + `(do + (when (:owners ~expected-acl) + (is (= (set (:owners ~expected-acl)) (set (:owners ~actual-acl))))) + (when (:edit-acl ~expected-acl) + (is (= (set (:edit-acl ~expected-acl)) (set (:edit-acl ~actual-acl))))) + (when (:edit-data ~expected-acl) + (is (= (set (:edit-data ~expected-acl)) (set (:edit-data ~actual-acl))))) + (when (:edit-meta ~expected-acl) + (is (= (set (:edit-meta ~expected-acl)) (set (:edit-meta ~actual-acl))))) + (when (:view-acl ~expected-acl) + (is (= (set (:view-acl ~expected-acl)) (set (:view-acl ~actual-acl))))) + (when (:view-data ~expected-acl) + (is (= (set (:view-data ~expected-acl)) (set (:view-data ~actual-acl))))) + (when (:view-meta ~expected-acl) + (is (= (set (:view-meta ~expected-acl)) (set (:view-meta ~actual-acl))))) + (when (:manage ~expected-acl) + (is (= (set (:manage ~expected-acl)) (set (:manage ~actual-acl))))) + (when (:delete ~expected-acl) + (is (= (set (:delete ~expected-acl)) (set (:delete ~actual-acl))))))) + + +;; +;; events +;; +(defmacro is-event + [expected-event actual-event] + `(let [expected-authn-info# (:authn-info ~expected-event) + authn-info# (:authn-info ~actual-event)] + (is (some? ~actual-event)) + (when (:name ~expected-event) + (is (= (:name ~expected-event) (:name ~actual-event)))) + (when (:description ~expected-event) + (is (= (:description ~expected-event) (:description ~actual-event)))) + (when (:category ~expected-event) + (is (= (:category ~expected-event) (:category ~actual-event)))) + (when expected-authn-info# + (is (= (:user-id expected-authn-info#) (:user-id authn-info#))) + (is (= (:active-claim expected-authn-info#) (:active-claim authn-info#))) + (is (= (set (:claims expected-authn-info#)) (set (:claims authn-info#))))) + (when (:linked-identifiers ~expected-event) + (is (= (set (:linked-identifiers ~expected-event)) + (set (get-in ~actual-event [:content :linked-identifiers]))))) + (when (some? (:success ~expected-event)) + (is (= (:success ~expected-event) (:success ~actual-event)))) + (when (some? (:acl ~expected-event)) + (is-acl (:acl ~expected-event) (:acl ~actual-event))))) + + +(defmacro is-last-event + [resource-id expected-event] + `(let [event# (last (event-utils/query-events ~resource-id {:orderby [["timestamp" :desc]] :last 1}))] + (is-event ~expected-event event#))) + + +(defmacro are-last-events + [resource-id expected-events] + `(let [events# (take (count ~expected-events) (event-utils/query-events ~resource-id {:orderby [["timestamp" :desc]] + :last (count ~expected-events)}))] + (is (= (count ~expected-events) (count events#))) + (doall (map (fn [expected-event# actual-event#] + (is-event expected-event# actual-event#)) + ~expected-events + events#)))) diff --git a/code/test/sixsq/nuvla/server/resources/module_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/module_lifecycle_test.clj index 95bc25dc7..c6c00d7f0 100644 --- a/code/test/sixsq/nuvla/server/resources/module_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/module_lifecycle_test.clj @@ -20,310 +20,477 @@ (defn- get-path-segments [path] (reduce - (fn [acu cur] - (conj acu (if (seq acu) - (str (last acu) "/" cur) - cur))) - [] - (str/split path #"/"))) + (fn [acu cur] + (conj acu (if (seq acu) + (str (last acu) "/" cur) + cur))) + [] + (str/split path #"/"))) (defn create-parent-projects [path user] (let [paths (get-path-segments (utils/get-parent-path path))] (run! - (fn [path-segment] - (-> user - (request base-uri - :request-method :post - :body (json/write-str {:subtype utils/subtype-project - :path path-segment - :parent-path (utils/get-parent-path path-segment)})) - ltu/body->edn - (ltu/is-status 201))) - paths))) - -(defn lifecycle-test-module + (fn [path-segment] + (-> user + (request base-uri + :request-method :post + :body (json/write-str {:subtype utils/subtype-project + :path path-segment + :parent-path (utils/get-parent-path path-segment)})) + ltu/body->edn + (ltu/is-status 201))) + paths))) + +(def session-anon + (-> (session (ltu/ring-app)) + (content-type "application/json"))) +(def session-admin + (header session-anon authn-info-header + "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon")) +(def session-user + (header session-anon authn-info-header + "user/jane user/jane group/nuvla-user group/nuvla-anon")) + +(def authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]}) +(def authn-info-jane {:user-id "user/jane" + :active-claim "user/jane" + :claims ["group/nuvla-anon" "user/jane" "group/nuvla-user"]}) +(def authn-info-anon {:claims ["group/nuvla-anon"]}) + +(def admin-group-name "Nuvla Administrator Group") + + +(defn build-valid-entry [subtype valid-content] - (let [session-anon (-> (session (ltu/ring-app)) - (content-type "application/json")) - session-admin (header session-anon authn-info-header - "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") - session-user (header session-anon authn-info-header - "user/jane user/jane group/nuvla-user group/nuvla-anon") - - valid-entry {:parent-path "a/b" - :path "a/b/c" - :subtype subtype - - :compatibility "docker-compose" - - :logo-url "https://example.org/logo" - - :data-accept-content-types ["application/json" "application/x-something"] - :data-access-protocols ["http+s3" "posix+nfs"] - - :content valid-content}] - - ;; create: NOK for anon + {:parent-path "a/b" + :path "a/b/c" + :subtype subtype + + :compatibility "docker-compose" + + :logo-url "https://example.org/logo" + + :data-accept-content-types ["application/json" "application/x-something"] + :data-access-protocols ["http+s3" "posix+nfs"] + + :content valid-content}) + +(defn create-module-nok + [valid-entry] + ;; create: NOK for anon + (-> session-anon + (request base-uri + :request-method :post + :body (json/write-str valid-entry)) + (ltu/body->edn) + (ltu/is-status 403)) + + (ltu/is-last-event nil + {:name "module.add" + :description "module.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin"]}}) + + ;; queries: NOK for anon + (-> session-anon + (request base-uri) + (ltu/body->edn) + (ltu/is-status 403)) + + (doseq [session [session-admin session-user]] + (-> session + (request base-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/is-count zero?))) + + ;; Creating editable parent project + (create-parent-projects (:path valid-entry) session-user) + + ;; invalid module subtype + (-> session-admin + (request base-uri + :request-method :post + :body (json/write-str (assoc valid-entry :subtype "bad-module-subtype"))) + (ltu/body->edn) + (ltu/is-status 400)) + + (ltu/is-last-event nil + {:name "module.add" + :description "module.add attempt failed." + :category "add" + :success false + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners ["group/nuvla-admin"]}}) + + (when (utils/is-application? valid-entry) + + (testing "application should have compatibility attribute set" + (-> session-user + (request base-uri + :request-method :post + :body (json/write-str (dissoc valid-entry :compatibility))) + (ltu/body->edn) + (ltu/is-status 400) + (ltu/message-matches "Application subtype should have compatibility attribute set!"))))) + + +(defn create-module + [session valid-entry event-owners authn-info user-name-or-id] + (let [uri (-> session + (request base-uri + :request-method :post + :body (json/write-str valid-entry)) + (ltu/body->edn) + (ltu/is-status 201) + (ltu/location)) + + abs-uri (str p/service-context uri)] + + (ltu/is-last-event uri + {:name "module.add" + :description (str user-name-or-id " added module " uri ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + + ;; retrieve: NOK for anon (-> session-anon - (request base-uri - :request-method :post - :body (json/write-str valid-entry)) + (request abs-uri) (ltu/body->edn) (ltu/is-status 403)) - ;; queries: NOK for anon + uri)) + +(defn retrieve-module + [uri valid-entry valid-content] + (let [abs-uri (str p/service-context uri) + {:keys [content] :as module} (-> session-admin + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/is-key-value :compatibility "docker-compose") + (as-> m (if (utils/is-application? valid-entry) + (ltu/is-operation-present m :validate-docker-compose) + (ltu/is-operation-absent m :validate-docker-compose))) + (ltu/body))] + (is (= valid-content (select-keys content (keys valid-content)))) + module)) + + +(defn edit-module + [uri valid-entry event-owners] + (let [abs-uri (str p/service-context uri)] + ;; edit: NOK for anon (-> session-anon - (request base-uri) + (request abs-uri + :request-method :put + :body (json/write-str valid-entry)) (ltu/body->edn) (ltu/is-status 403)) - (doseq [session [session-admin session-user]] + (ltu/is-last-event uri + {:name "module.edit" + :description "module.edit attempt failed." + :category "edit" + :success false + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners event-owners}}) + + ;; insert 5 more versions + (doseq [_ (range 5)] + (-> session-admin + (request abs-uri + :request-method :put + :body (json/write-str valid-entry)) + (ltu/body->edn) + (ltu/is-status 200)) + + (ltu/is-last-event uri + {:name "module.edit" + :description (str admin-group-name " edited module " uri ".") + :category "edit" + :success true + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners event-owners}})) + + (let [versions (-> session-admin + (request abs-uri + :request-method :put + :body (json/write-str valid-entry)) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body) + :versions)] + (is (= 7 (count versions))) + + ;; extract by indexes or last + (doseq [[i n] [["_0" 0] ["_1" 1] ["" 6]]] + (let [content-id (-> session-admin + (request (str abs-uri i)) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body) + :content + :id)] + (is (= (-> versions (nth n) :href) content-id)) + (is (= (-> versions (nth n) :author) "someone")) + (is (= (-> versions (nth n) :commit) "wip"))))))) + + +(defn publish-unpublish + [session uri event-owners authn-info user-name-or-id] + ;; publish + (let [abs-uri (str p/service-context uri) + publish-url (-> session + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/is-operation-present :publish) + (ltu/is-operation-present :unpublish) + (ltu/get-op-url :publish))] + + (testing "publish last version" (-> session - (request base-uri) + (request publish-url) (ltu/body->edn) (ltu/is-status 200) - (ltu/is-count zero?))) - - ;; Creating editable parent project - (create-parent-projects (:path valid-entry) session-user) + (ltu/message-matches "published successfully")) + + (ltu/is-last-event uri + {:name "module.publish" + :description (str user-name-or-id " executed action publish on module " uri ".") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) + + (testing "operation urls of specific version" + (let [abs-uri-v2 (str abs-uri "_2") + resp (-> session + (request (str abs-uri "_2")) + (ltu/body->edn) + (ltu/is-status 200)) + publish-url (ltu/get-op-url resp :publish) + unpublish-url (ltu/get-op-url resp :unpublish) + edit-url (ltu/get-op-url resp :edit) + delete-url (ltu/get-op-url resp :delete) + delete-version-url (ltu/get-op-url resp :delete-version)] + (is (= publish-url (str abs-uri-v2 "/publish"))) + (is (= unpublish-url (str abs-uri-v2 "/unpublish"))) + (is (= delete-version-url (str abs-uri-v2 "/delete-version"))) + (is (= delete-url abs-uri)) + (is (= edit-url abs-uri)))) + + (testing "publish specific version" + (-> session + (request (str abs-uri "_2/publish")) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/message-matches "published successfully")) + + (ltu/is-last-event (str uri "_2") + {:name "module.publish" + :description (str user-name-or-id " executed action publish on module " uri "_2.") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}})) + + (let [unpublish-url (-> session + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/is-operation-present :publish) + (ltu/is-operation-present :unpublish) + (ltu/is-key-value #(-> % last :published) :versions true) + (ltu/is-key-value #(-> % (nth 2) :published) :versions true) + (ltu/is-key-value :published true) + (ltu/get-op-url :unpublish))] - ;; invalid module subtype - (-> session-admin - (request base-uri - :request-method :post - :body (json/write-str (assoc valid-entry :subtype "bad-module-subtype"))) + (-> session + (request unpublish-url) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/message-matches "unpublished successfully"))) + + (ltu/is-last-event uri + {:name "module.unpublish" + :description (str user-name-or-id " executed action unpublish on module " uri ".") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + + ; publish is idempotent + (-> session + (request (str abs-uri "_2/publish")) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/message-matches "published successfully")) + + (ltu/is-last-event (str uri "_2") + {:name "module.publish" + :description (str user-name-or-id " executed action publish on module " uri "_2.") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + + (-> session + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/is-operation-present :publish) + (ltu/is-operation-present :unpublish) + (ltu/is-key-value #(-> % last :published) :versions false) + (ltu/is-key-value :published true) + (ltu/get-op-url :unpublish)) + + (-> session + (request (str abs-uri "_2/unpublish")) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/message-matches "unpublished successfully")) + + (ltu/is-last-event (str uri "_2") + {:name "module.unpublish" + :description (str user-name-or-id " executed action unpublish on module " uri "_2.") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners event-owners}}) + + (-> session + (request abs-uri) (ltu/body->edn) - (ltu/is-status 400)) + (ltu/is-status 200) + (ltu/is-operation-present :publish) + (ltu/is-operation-present :unpublish) + (ltu/is-key-value #(-> % (nth 2) :published) :versions false) + (ltu/is-key-value :published false) + (ltu/get-op-url :unpublish)))) + + +(defn versions + [uri valid-entry event-owners] + (let [abs-uri (str p/service-context uri)] + (testing "edit module without putting the module-content should not create new version" + (is (= 7 (-> session-admin + (request abs-uri + :request-method :put + :body (json/write-str (dissoc valid-entry :content :path))) + (ltu/body->edn) + (ltu/is-status 200) + (ltu/body) + :versions + count)))) + + (doseq [i ["_0/delete-version" "_1/delete-version"]] + (-> session-admin + (request (str abs-uri i)) + (ltu/body->edn) + (ltu/is-status 200)) - (when (utils/is-application? valid-entry) - (testing "application should have compatibility attribute set" - (-> session-user - (request base-uri - :request-method :post - :body (json/write-str (dissoc valid-entry :compatibility))) - (ltu/body->edn) - (ltu/is-status 400) - (ltu/message-matches "Application subtype should have compatibility attribute set!")))) + (-> session-admin + (request (str abs-uri i)) + (ltu/body->edn) + (ltu/is-status 404))) - ;; adding, retrieving and deleting entry as user should succeed - (doseq [session [session-admin session-user]] - (let [uri (-> session - (request base-uri - :request-method :post - :body (json/write-str valid-entry)) - (ltu/body->edn) - (ltu/is-status 201) - (ltu/location)) - abs-uri (str p/service-context uri)] + (testing "delete latest version without specifying version" + (-> session-admin + (request (str abs-uri "/delete-version")) + (ltu/body->edn) + (ltu/is-status 200))) - ;; retrieve: NOK for anon - (-> session-anon - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 403)) - - (let [content (-> session-admin - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/is-key-value :compatibility "docker-compose") - (as-> m (if (utils/is-application? valid-entry) - (ltu/is-operation-present m :validate-docker-compose) - (ltu/is-operation-absent m :validate-docker-compose))) - (ltu/body) - :content)] - (is (= valid-content (select-keys content (keys valid-content))))) - - ;; edit: NOK for anon - (-> session-anon - (request abs-uri - :request-method :put - :body (json/write-str valid-entry)) - (ltu/body->edn) - (ltu/is-status 403)) - - ;; insert 5 more versions - (doseq [_ (range 5)] - (-> session-admin - (request abs-uri - :request-method :put - :body (json/write-str valid-entry)) - (ltu/body->edn) - (ltu/is-status 200))) - - (let [versions (-> session-admin - (request abs-uri - :request-method :put - :body (json/write-str valid-entry)) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body) - :versions)] - (is (= 7 (count versions))) - - ;; extract by indexes or last - (doseq [[i n] [["_0" 0] ["_1" 1] ["" 6]]] - (let [content-id (-> session-admin - (request (str abs-uri i)) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body) - :content - :id)] - (is (= (-> versions (nth n) :href) content-id)) - (is (= (-> versions (nth n) :author) "someone")) - (is (= (-> versions (nth n) :commit) "wip"))))) - - ;; publish - (let [publish-url (-> session - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/is-operation-present :publish) - (ltu/is-operation-present :unpublish) - (ltu/get-op-url :publish))] - - (testing "publish last version" - (-> session - (request publish-url) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/message-matches "published successfully"))) - - (testing "operation urls of specific version" - (let [abs-uri-v2 (str abs-uri "_2") - resp (-> session - (request (str abs-uri "_2")) - (ltu/body->edn) - (ltu/is-status 200)) - publish-url (ltu/get-op-url resp :publish) - unpublish-url (ltu/get-op-url resp :unpublish) - edit-url (ltu/get-op-url resp :edit) - delete-url (ltu/get-op-url resp :delete) - delete-version-url (ltu/get-op-url resp :delete-version)] - (is (= publish-url (str abs-uri-v2 "/publish"))) - (is (= unpublish-url (str abs-uri-v2 "/unpublish"))) - (is (= delete-version-url (str abs-uri-v2 "/delete-version"))) - (is (= delete-url abs-uri)) - (is (= edit-url abs-uri)))) - - (testing "publish specific version" - (-> session - (request (str abs-uri "_2/publish")) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/message-matches "published successfully"))) - - (let [unpublish-url (-> session - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/is-operation-present :publish) - (ltu/is-operation-present :unpublish) - (ltu/is-key-value #(-> % last :published) :versions true) - (ltu/is-key-value #(-> % (nth 2) :published) :versions true) - (ltu/is-key-value :published true) - (ltu/get-op-url :unpublish))] - - (-> session - (request unpublish-url) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/message-matches "unpublished successfully"))) - - ; publish is idempotent - (-> session - (request (str abs-uri "_2/publish")) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/message-matches "published successfully")) - - (-> session - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/is-operation-present :publish) - (ltu/is-operation-present :unpublish) - (ltu/is-key-value #(-> % last :published) :versions false) - (ltu/is-key-value :published true) - (ltu/get-op-url :unpublish)) - - (-> session - (request (str abs-uri "_2/unpublish")) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/message-matches "unpublished successfully")) - - (-> session - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/is-operation-present :publish) - (ltu/is-operation-present :unpublish) - (ltu/is-key-value #(-> % (nth 2) :published) :versions false) - (ltu/is-key-value :published false) - (ltu/get-op-url :unpublish))) - - (testing "edit module without putting the module-content should not create new version" - (is (= 7 (-> session-admin - (request abs-uri - :request-method :put - :body (json/write-str (dissoc valid-entry :content :path))) - (ltu/body->edn) - (ltu/is-status 200) - (ltu/body) - :versions - count)))) - (doseq [i ["_0/delete-version" "_1/delete-version"]] - (-> session-admin - (request (str abs-uri i)) - (ltu/body->edn) - (ltu/is-status 200)) + (ltu/is-last-event uri + {:name "module.delete-version" + :description (str admin-group-name " executed action delete-version on module " uri ".") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners event-owners}}) - (-> session-admin - (request (str abs-uri i)) - (ltu/body->edn) - (ltu/is-status 404))) + (testing "delete out of bound index should return 404" + (-> session-admin + (request (str abs-uri "_50/delete-version")) + (ltu/body->edn) + (ltu/is-status 404))) - (testing "delete latest version without specifying version" - (-> session-admin - (request (str abs-uri "/delete-version")) - (ltu/body->edn) - (ltu/is-status 200))) + (-> session-admin + (request (str abs-uri "_50")) + (ltu/body->edn) + (ltu/is-status 404)))) - (testing "delete out of bound index should return 404" - (-> session-admin - (request (str abs-uri "_50/delete-version")) - (ltu/body->edn) - (ltu/is-status 404))) - (-> session-admin - (request (str abs-uri "_50")) - (ltu/body->edn) - (ltu/is-status 404)) +(defn delete-module + [uri event-owners] + (let [abs-uri (str p/service-context uri)] + ;; delete: NOK for anon + (-> session-anon + (request abs-uri + :request-method :delete) + (ltu/body->edn) + (ltu/is-status 403)) - ;; delete: NOK for anon - (-> session-anon - (request abs-uri - :request-method :delete) - (ltu/body->edn) - (ltu/is-status 403)) + (-> session-admin + (request abs-uri + :request-method :delete) + (ltu/body->edn) + (ltu/is-status 200)) - (-> session-admin - (request abs-uri - :request-method :delete) - (ltu/body->edn) - (ltu/is-status 200)) - ;; verify that the resource was deleted. - (-> session-admin - (request abs-uri) - (ltu/body->edn) - (ltu/is-status 404)))))) + (ltu/is-last-event uri + {:name "module.delete" + :description (str admin-group-name " deleted module " uri ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info-admin + :acl {:owners event-owners}}) + + ;; verify that the resource was deleted. + (-> session-admin + (request abs-uri) + (ltu/body->edn) + (ltu/is-status 404)))) + + +(defn lifecycle-test-module + [subtype valid-content] + (let [valid-entry (build-valid-entry subtype valid-content)] + (create-module-nok valid-entry) + ;; adding, retrieving and deleting entry as user should succeed + (doseq [[session event-owners authn-info user-name-or-id] + [[session-admin ["group/nuvla-admin"] authn-info-admin admin-group-name] + [session-user ["group/nuvla-admin" "user/jane"] authn-info-jane "user/jane"]]] + (let [uri (create-module session valid-entry event-owners authn-info user-name-or-id) + _module (retrieve-module uri valid-entry valid-content)] + (edit-module uri valid-entry event-owners) + (publish-unpublish session uri event-owners authn-info user-name-or-id) + (versions uri valid-entry event-owners) + (delete-module uri event-owners))))) + (deftest lifecycle-component (let [valid-component {:author "someone" @@ -353,12 +520,12 @@ session-user (header session-anon authn-info-header "user/jane user/jane group/nuvla-user group/nuvla-anon") - project {:resource-type module/resource-type - :created timestamp - :updated timestamp - :parent-path "" - :path "example" - :subtype utils/subtype-project} + project {:resource-type module/resource-type + :created timestamp + :updated timestamp + :parent-path "" + :path "example" + :subtype utils/subtype-project} valid-app {:parent-path "example" :path "example/app" @@ -393,13 +560,13 @@ (testing "Failure creating application 3: user does not have edit rights in parent project" ;; Creating a parent project with nuvla-admin as owner - (let [uri (-> session-admin - (request base-uri - :request-method :post - :body (json/write-str project)) - ltu/body->edn - (ltu/is-status 201) - ltu/location-url)] + (let [uri (-> session-admin + (request base-uri + :request-method :post + :body (json/write-str project)) + ltu/body->edn + (ltu/is-status 201) + ltu/location-url)] ;; If user has no view rights, failure message says that parent project does not exist. (-> session-user @@ -415,11 +582,11 @@ (request uri :request-method :put :body (json/write-str - (assoc project - :acl {:owners ["group/nuvla-admin"] - :view-meta ["user/jane"] - :view-data ["user/jane"] - :view-acl ["user/jane"]}))) + (assoc project + :acl {:owners ["group/nuvla-admin"] + :view-meta ["user/jane"] + :view-data ["user/jane"] + :view-acl ["user/jane"]}))) ltu/body->edn (ltu/is-status 200))) @@ -432,7 +599,7 @@ (ltu/is-status 403) (ltu/message-matches "You do not have edit rights for:"))) - ;; Trying to add app to parent app should fail + ;; Trying to add app to parent app should fail (testing "Failure creating application 4: Parent is not a project." (-> session-user (request base-uri @@ -456,18 +623,18 @@ (testing "new application can be in a project nested inside another project" ;; Creating a parent project with wrong edit rights - (-> session-user - (request base-uri - :request-method :post - :body (json/write-str (assoc project :path "grandparent"))) - ltu/body->edn - (ltu/is-status 201)) - (-> session-user - (request base-uri - :request-method :post - :body (json/write-str (assoc project :path "grandparent/parent"))) - ltu/body->edn - (ltu/is-status 201)) + (-> session-user + (request base-uri + :request-method :post + :body (json/write-str (assoc project :path "grandparent"))) + ltu/body->edn + (ltu/is-status 201)) + (-> session-user + (request base-uri + :request-method :post + :body (json/write-str (assoc project :path "grandparent/parent"))) + ltu/body->edn + (ltu/is-status 201)) (-> session-user (request base-uri :request-method :post @@ -529,9 +696,9 @@ (request app-1-uri :request-method :put :body (json/write-str - (update valid-app-1 :content assoc - :docker-compose "content changed" - :commit "second commit"))) + (update valid-app-1 :content assoc + :docker-compose "content changed" + :commit "second commit"))) (ltu/body->edn) (ltu/is-status 200)) diff --git a/code/test/sixsq/nuvla/server/resources/nuvlabox_0_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/nuvlabox_0_lifecycle_test.clj index a1a086619..cba6289ed 100644 --- a/code/test/sixsq/nuvla/server/resources/nuvlabox_0_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/nuvlabox_0_lifecycle_test.clj @@ -81,6 +81,8 @@ :owners ["group/nuvla-admin"] }}) +(def admin-group-name "Nuvla Administrator Group") + (deftest check-metadata (mdtu/check-metadata-exists nb/resource-type @@ -93,7 +95,10 @@ session (content-type "application/json")) - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon")] + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + authn-info {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]}] (let [nuvlabox-id (-> session-owner (request base-uri @@ -104,6 +109,14 @@ (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str "user/alpha added nuvlabox " nuvlabox-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) {:keys [id acl owner]} (-> session-owner (request nuvlabox-url) (ltu/body->edn) @@ -139,10 +152,28 @@ (ltu/is-key-value :edit-acl :acl (conj (:edit-acl acl) new-owner)) (ltu/body))) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.edit" + :description "user/alpha edited nuvlabox name NB changed." + :category "edit" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha" "user/beta"]}}) + (-> session-owner (request nuvlabox-url :request-method :delete) - (ltu/is-status 200))) + (ltu/is-status 200)) + + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.delete" + :description "user/alpha deleted nuvlabox name NB changed." + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha" "user/beta"]}})) ;; create nuvlabox with inexistent vpn id will fail (-> session-owner @@ -157,15 +188,26 @@ (deftest create-activate-decommission-delete-lifecycle (binding [config-nuvla/*stripe-api-key* nil] - (let [session (-> (ltu/ring-app) - session - (content-type "application/json")) - session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") - - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") - session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon")] - - (doseq [session [session-admin session-owner]] + (let [session (-> (ltu/ring-app) + session + (content-type "application/json")) + session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") + + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-owner {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] + + (doseq [[session authn-info user-name-or-id] + [[session-admin authn-info-admin admin-group-name] + [session-owner authn-info-owner "user/alpha"]]] (let [nuvlabox-id (-> session (request base-uri :request-method :post @@ -174,6 +216,14 @@ (ltu/is-status 201) (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str user-name-or-id " added nuvlabox " nuvlabox-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) activate-url (-> session (request nuvlabox-url) @@ -199,6 +249,15 @@ :api-key (ltu/href->url)) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.activate" + :description "user/unknown activated nuvlabox." + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + credential-nuvlabox (-> session-admin (request credential-url) (ltu/body->edn) @@ -258,11 +317,21 @@ (ltu/is-key-value :state "ACTIVATED") (ltu/get-op-url :decommission))] - (-> session - (request decommission-url - :request-method :post) - (ltu/body->edn) - (ltu/is-status 202)) + (let [job-id (-> session + (request decommission-url + :request-method :post) + (ltu/body->edn) + (ltu/is-status 202) + (ltu/location))] + + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.decommission" + :description (str user-name-or-id " decommissioned nuvlabox.") + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin"]}})) ;; verify state of the resource and that ACL has been updated (let [{:keys [owner acl]} (-> session @@ -312,6 +381,15 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.delete" + :description (str user-name-or-id " deleted nuvlabox " nuvlabox-id ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin"]}}) + ;; verify that the nuvlabox has been removed (-> session (request nuvlabox-url) @@ -321,15 +399,26 @@ (deftest create-activate-commission-decommission-error-delete-lifecycle (binding [config-nuvla/*stripe-api-key* nil] - (let [session (-> (ltu/ring-app) - session - (content-type "application/json")) - session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") - - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") - session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon")] - - (doseq [session [session-admin session-owner]] + (let [session (-> (ltu/ring-app) + session + (content-type "application/json")) + session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") + + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-owner {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] + + (doseq [[session authn-info user-name-or-id] + [[session-admin authn-info-admin admin-group-name] + [session-owner authn-info-owner "user/alpha"]]] (let [nuvlabox-id (-> session (request base-uri :request-method :post @@ -339,6 +428,15 @@ (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str user-name-or-id " added nuvlabox " nuvlabox-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + activate-url (-> session (request nuvlabox-url) (ltu/body->edn) @@ -362,6 +460,15 @@ :api-key (ltu/href->url)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.activate" + :description "user/unknown activated nuvlabox." + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + (let [{isg-id :id} (-> session-admin (content-type "application/x-www-form-urlencoded") (request isg-collection-uri @@ -401,6 +508,15 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.commission" + :description (str user-name-or-id " commissioned nuvlabox.") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + ;; verify state of the resource (-> session (request nuvlabox-url) @@ -556,7 +672,34 @@ (ltu/body->edn) (ltu/is-status 201) (ltu/body) - :resource-id)] + :resource-id) + + action (fn [action-url action-id method body event-description] + (let [job-id (-> (case method + :get + (request session action-url) + :post + (request session + action-url + :request-method :post + :body (json/write-str body)) + nil) + (ltu/body->edn) + (ltu/is-status 202) + (ltu/location))] + + (ltu/is-last-event nuvlabox-id + {:name (str "nuvlabox." action-id) + :description (str user-name-or-id " " event-description ".") + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}))) + action-get (fn [action-url action-id event-description] + (action action-url action-id :get nil event-description)) + action-post (fn [action-url action-id body event-description] + (action action-url action-id :post body event-description))] ;; check-api action (-> session @@ -565,32 +708,16 @@ (ltu/is-status 202)) ;; reboot action - (-> session - (request reboot) - (ltu/body->edn) - (ltu/is-status 202)) + (action-get reboot "reboot" "rebooted nuvlabox") ;; add-ssh-key action - (-> session - (request add-ssh-key) - (ltu/body->edn) - (ltu/is-status 202)) + (action-get add-ssh-key "add-ssh-key" "added ssh key to nuvlabox") ;; revoke-ssh-key action - (-> session - (request revoke-ssh-key - :request-method :post - :body (json/write-str {:credential aux-ssh-cred})) - (ltu/body->edn) - (ltu/is-status 202)) + (action-post revoke-ssh-key "revoke-ssh-key" {:credential aux-ssh-cred} "revoked ssh key from nuvlabox") ;; update-nuvlabox-action - (-> session - (request update-nuvlabox - :request-method :post - :body (json/write-str {:nuvlabox-release nuvlabox-release})) - (ltu/body->edn) - (ltu/is-status 202))) + (action-post update-nuvlabox "update-nuvlabox" {:nuvlabox-release nuvlabox-release} "updated nuvlabox")) ;; second commissioning of the resource (with swarm credentials) (-> session diff --git a/code/test/sixsq/nuvla/server/resources/nuvlabox_1_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/nuvlabox_1_lifecycle_test.clj index b8be0e601..cf5766d64 100644 --- a/code/test/sixsq/nuvla/server/resources/nuvlabox_1_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/nuvlabox_1_lifecycle_test.clj @@ -68,6 +68,9 @@ (str nb/resource-type "-" nb-1/schema-version))) +(def admin-group-name "Nuvla Administrator Group") + + (deftest create-edit-delete-lifecycle ;; Disable stripe (binding [config-nuvla/*stripe-api-key* nil] @@ -75,7 +78,10 @@ session (content-type "application/json")) - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon")] + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + authn-info {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]}] (let [nuvlabox-id (-> session-owner (request base-uri @@ -86,6 +92,14 @@ (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str "user/alpha added nuvlabox " nuvlabox-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) {:keys [id acl owner]} (-> session-owner (request nuvlabox-url) (ltu/body->edn) @@ -121,15 +135,26 @@ (deftest create-activate-decommission-delete-lifecycle (binding [config-nuvla/*stripe-api-key* nil] - (let [session (-> (ltu/ring-app) - session - (content-type "application/json")) - session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") - - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") - session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon")] - - (doseq [session [session-admin session-owner]] + (let [session (-> (ltu/ring-app) + session + (content-type "application/json")) + session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") + + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-owner {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] + + (doseq [[session authn-info user-name-or-id] + [[session-admin authn-info-admin admin-group-name] + [session-owner authn-info-owner "user/alpha"]]] (let [nuvlabox-id (-> session (request base-uri :request-method :post @@ -139,6 +164,14 @@ (ltu/is-status 201) (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str user-name-or-id " added nuvlabox " nuvlabox-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) activate-url (-> session (request nuvlabox-url) @@ -164,6 +197,15 @@ :api-key (ltu/href->url)) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.activate" + :description "user/unknown activated nuvlabox." + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + credential-nuvlabox (-> session-admin (request credential-url) (ltu/body->edn) @@ -223,11 +265,21 @@ (ltu/is-key-value :state "ACTIVATED") (ltu/get-op-url :decommission))] - (-> session - (request decommission-url - :request-method :post) - (ltu/body->edn) - (ltu/is-status 202)) + (let [job-id (-> session + (request decommission-url + :request-method :post) + (ltu/body->edn) + (ltu/is-status 202) + (ltu/location))] + + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.decommission" + :description (str user-name-or-id " decommissioned nuvlabox.") + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin"]}})) ;; verify state of the resource and that ACL has been updated (let [{:keys [owner acl]} (-> session @@ -277,6 +329,15 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.delete" + :description (str user-name-or-id " deleted nuvlabox " nuvlabox-id ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin"]}}) + ;; verify that the nuvlabox has been removed (-> session (request nuvlabox-url) @@ -286,16 +347,27 @@ (deftest create-activate-commission-decommission-error-delete-lifecycle (binding [config-nuvla/*stripe-api-key* nil] - (let [session (-> (ltu/ring-app) - session - (content-type "application/json")) - session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") - - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") - session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") - tags #{"tag-1", "tag-2"}] - - (doseq [session [session-admin session-owner]] + (let [session (-> (ltu/ring-app) + session + (content-type "application/json")) + session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") + + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-owner {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}} + tags #{"tag-1", "tag-2"}] + + (doseq [[session authn-info user-name-or-id] + [[session-admin authn-info-admin admin-group-name] + [session-owner authn-info-owner "user/alpha"]]] (let [nuvlabox-id (-> session (request base-uri :request-method :post @@ -306,6 +378,15 @@ (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str user-name-or-id " added nuvlabox " nuvlabox-id ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + activate-url (-> session (request nuvlabox-url) (ltu/body->edn) @@ -329,6 +410,15 @@ :api-key (ltu/href->url)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.activate" + :description "user/unknown activated nuvlabox." + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + (let [{isg-id :id} (-> session-admin (content-type "application/x-www-form-urlencoded") (request isg-collection-uri @@ -371,6 +461,15 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.commission" + :description (str user-name-or-id " commissioned nuvlabox.") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + ;; verify state of the resource (-> session (request nuvlabox-url) diff --git a/code/test/sixsq/nuvla/server/resources/nuvlabox_2_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/nuvlabox_2_lifecycle_test.clj index 53c2fc97e..c9d8f59b4 100644 --- a/code/test/sixsq/nuvla/server/resources/nuvlabox_2_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/nuvlabox_2_lifecycle_test.clj @@ -68,6 +68,9 @@ :capabilities ["RANDOM" "NUVLA_JOB_PULL"]}) +(def admin-group-name "Nuvla Administrator Group") + + (deftest check-metadata (mdtu/check-metadata-exists nb/resource-type (str nb/resource-type "-" nb-2/schema-version))) @@ -80,7 +83,10 @@ session (content-type "application/json")) - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon")] + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + authn-info {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]}] (let [nuvlabox-id (-> session-owner (request base-uri @@ -90,6 +96,14 @@ (ltu/is-status 201) (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str "user/alpha added nuvlabox " nb-name ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) {:keys [id acl owner]} (-> session-owner (request nuvlabox-url) @@ -111,7 +125,16 @@ (-> session-owner (request nuvlabox-url :request-method :delete) - (ltu/is-status 200))) + (ltu/is-status 200)) + + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.delete" + :description (str "user/alpha deleted nuvlabox " nb-name ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}})) ;; create nuvlabox with inexistent vpn id will fail (-> session-owner @@ -126,14 +149,25 @@ (deftest create-activate-create-log-decommission-delete-lifecycle (binding [config-nuvla/*stripe-api-key* nil] - (let [session (-> (ltu/ring-app) - session - (content-type "application/json")) - session-admin (header session authn-info-header (str "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon " session-id)) - session-owner (header session authn-info-header (str "user/alpha user/alpha group/nuvla-user group/nuvla-anon " session-id)) - session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon")] - - (doseq [session [session-admin session-owner]] + (let [session (-> (ltu/ring-app) + session + (content-type "application/json")) + session-admin (header session authn-info-header (str "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon " session-id)) + session-owner (header session authn-info-header (str "user/alpha user/alpha group/nuvla-user group/nuvla-anon " session-id)) + session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user" session-id]} + authn-info-owner {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user" session-id]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}}] + + (doseq [[session authn-info user-name-or-id] + [[session-admin authn-info-admin admin-group-name] + [session-owner authn-info-owner "user/alpha"]]] (let [nuvlabox-id (-> session (request base-uri :request-method :post @@ -143,6 +177,14 @@ (ltu/is-status 201) (ltu/location)) nuvlabox-url (str p/service-context nuvlabox-id) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.add" + :description (str user-name-or-id " added nuvlabox " nb-name ".") + :category "add" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) activate-url (-> session (request nuvlabox-url) @@ -169,6 +211,14 @@ (ltu/body) :api-key (ltu/href->url)) + _ (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.activate" + :description "user/unknown activated nuvlabox." + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) credential-nuvlabox (-> session-admin (request credential-url) @@ -251,11 +301,21 @@ (ltu/is-key-value :delete :acl ["group/nuvla-admin" session-id]) (ltu/is-key-value :view-acl :acl ["group/nuvla-admin" session-id])))) - (-> session - (request decommission-url - :request-method :post) - (ltu/body->edn) - (ltu/is-status 202)) + (let [job-id (-> session + (request decommission-url + :request-method :post) + (ltu/body->edn) + (ltu/is-status 202) + (ltu/location))] + + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.decommission" + :description (str user-name-or-id " decommissioned nuvlabox.") + :category "action" + :success true + :linked-identifiers [job-id] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin"]}})) ;; verify state of the resource and that ACL has been updated (let [{:keys [owner acl]} (-> session @@ -306,6 +366,15 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.delete" + :description (str user-name-or-id " deleted nuvlabox " nb-name ".") + :category "delete" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin"]}}) + ;; verify that the nuvlabox has been removed (-> session (request nuvlabox-url) @@ -363,16 +432,27 @@ (deftest create-activate-commission-decommission-error-delete-lifecycle (binding [config-nuvla/*stripe-api-key* nil] - (let [session (-> (ltu/ring-app) - session - (content-type "application/json")) - session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") - - session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") - session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") - tags #{"tag-1", "tag-2"}] - - (doseq [session [session-admin session-owner]] + (let [session (-> (ltu/ring-app) + session + (content-type "application/json")) + session-admin (header session authn-info-header "group/nuvla-admin group/nuvla-admin group/nuvla-user group/nuvla-anon") + + session-owner (header session authn-info-header "user/alpha user/alpha group/nuvla-user group/nuvla-anon") + session-anon (header session authn-info-header "user/unknown user/unknown group/nuvla-anon") + authn-info-admin {:user-id "group/nuvla-admin" + :active-claim "group/nuvla-admin" + :claims ["group/nuvla-admin" "group/nuvla-anon" "group/nuvla-user"]} + authn-info-owner {:user-id "user/alpha" + :active-claim "user/alpha" + :claims ["group/nuvla-anon" "user/alpha" "group/nuvla-user"]} + authn-info-anon {:user-id "user/unknown" + :active-claim "user/unknown" + :claims #{"user/unknown" "group/nuvla-anon"}} + tags #{"tag-1", "tag-2"}] + + (doseq [[session authn-info user-name-or-id] + [[session-admin authn-info-admin admin-group-name] + [session-owner authn-info-owner "user/alpha"]]] (let [nuvlabox-id (-> session (request base-uri :request-method :post @@ -406,6 +486,15 @@ :api-key (ltu/href->url)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.activate" + :description "user/unknown activated nuvlabox." + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info-anon + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + (let [{isg-id :id} (-> session-admin (content-type "application/x-www-form-urlencoded") (request isg-collection-uri @@ -448,6 +537,15 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event nuvlabox-id + {:name "nuvlabox.commission" + :description (str user-name-or-id " commissioned nuvlabox.") + :category "action" + :success true + :linked-identifiers [] + :authn-info authn-info + :acl {:owners ["group/nuvla-admin" "user/alpha"]}}) + ;; verify state of the resource (-> session (request nuvlabox-url) diff --git a/code/test/sixsq/nuvla/server/resources/session_api_key_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/session_api_key_lifecycle_test.clj index 01512d4d8..282306561 100644 --- a/code/test/sixsq/nuvla/server/resources/session_api_key_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/session_api_key_lifecycle_test.clj @@ -123,7 +123,10 @@ :key uuid :secret secret}} unauthorized-create (update-in valid-create [:template :secret] (constantly bad-digest)) - invalid-create (assoc-in valid-create [:template :invalid] "BAD")] + invalid-create (assoc-in valid-create [:template :invalid] "BAD") + event-authn-info {:user-id "user/unknown" + :active-claim "user/unknown" + :claims ["user/unknown" "group/nuvla-anon"]}] ;; anonymous query should succeed but have no entries (-> session-anon @@ -140,6 +143,14 @@ (ltu/body->edn) (ltu/is-status 403)) + (ltu/is-last-event uuid {:name "session.add" + :description "Login attempt failed." + :category "add" + :success false + :linked-identifiers [(str "credential/" uuid)] + :authn-info event-authn-info + :acl {:owners ["group/nuvla-admin"]}}) + ;; anonymous create must succeed; also with redirect (let [resp (-> session-anon (request base-uri @@ -149,6 +160,13 @@ (ltu/is-set-cookie) (ltu/is-status 201)) id (ltu/body-resource-id resp) + _ (ltu/is-last-event id {:name "session.add" + :description (str (:id valid-api-key) " logged in.") + :category "add" + :success true + :linked-identifiers [(str "credential/" uuid)] + :authn-info event-authn-info + :acl {:owners ["group/nuvla-admin" id]}}) token (get-in resp [:response :cookies authn-cookie :value]) cookie-info (if token (sign/unsign-cookie-info token) {}) @@ -221,7 +239,7 @@ ;; user with session role can delete resource (-> (session app) - (header authn-info-header (str "user group/nuvla-user " id)) + (header authn-info-header (str "user/user group/nuvla-user " id)) (request abs-uri :request-method :delete) (ltu/is-unset-cookie) diff --git a/code/test/sixsq/nuvla/server/resources/session_password_lifecycle_test.clj b/code/test/sixsq/nuvla/server/resources/session_password_lifecycle_test.clj index b4b771b2b..3b21871d6 100644 --- a/code/test/sixsq/nuvla/server/resources/session_password_lifecycle_test.clj +++ b/code/test/sixsq/nuvla/server/resources/session_password_lifecycle_test.clj @@ -5,6 +5,7 @@ [clojure.test :refer [deftest is testing use-fixtures]] [peridot.core :refer [content-type header request session]] [postal.core :as postal] + [sixsq.nuvla.auth.password :as auth-password] [sixsq.nuvla.auth.utils :as auth] [sixsq.nuvla.auth.utils.sign :as sign] [sixsq.nuvla.server.app.params :as p] @@ -150,20 +151,23 @@ :email "jane@example.org")] ; anonymous create must succeed - (let [resp (-> session-anon - (request base-uri - :request-method :post - :body (json/write-str valid-create)) - (ltu/body->edn) - (ltu/is-set-cookie) - (ltu/is-status 201)) - id (ltu/body-resource-id resp) - - token (get-in resp [:response :cookies authn-cookie :value]) - authn-info (if token (sign/unsign-cookie-info token) {}) - - uri (ltu/location resp) - abs-uri (str p/service-context uri)] + (let [resp (-> session-anon + (request base-uri + :request-method :post + :body (json/write-str valid-create)) + (ltu/body->edn) + (ltu/is-set-cookie) + (ltu/is-status 201)) + id (ltu/body-resource-id resp) + + token (get-in resp [:response :cookies authn-cookie :value]) + authn-info (if token (sign/unsign-cookie-info token) {}) + event-authn-info {:user-id "user/user" + :active-claim "group/nuvla-user" + :claims ["group/nuvla-anon" id "user/user"]} + + uri (ltu/location resp) + abs-uri (str p/service-context uri)] ; check claims in cookie (is (= jane-user-id (:user-id authn-info))) @@ -244,6 +248,18 @@ (ltu/body->edn) (ltu/is-status 200)) + (ltu/is-last-event id + {:name "session.delete" + :description (str "user/user logged out.") + :category "delete" + :success true + :linked-identifiers [] + :authn-info event-authn-info + :acl {:owners ["group/nuvla-admin" + "user/user"]}}) + + + ; create with invalid template fails (-> session-anon (request base-uri @@ -314,6 +330,17 @@ (ltu/is-status 201)) session-user-id (ltu/body-resource-id session-user) sesssion-user-url (ltu/location-url session-user) + credential-id (:credential-password (auth-password/user-id->user user-id)) + _ (ltu/is-last-event session-user-id + {:name "session.add" + :description (str username " logged in.") + :category "add" + :success true + :linked-identifiers [user-id credential-id] + :authn-info {:user-id "user/unknown" + :active-claim "user/unknown" + :claims ["user/unknown" "group/nuvla-anon"]} + :acl {:owners ["group/nuvla-admin" user-id]}}) handler (wrap-authn-info identity) authn-session-user (-> session-user :response @@ -328,7 +355,10 @@ switch-op-url (-> (apply request session-json (concat [sesssion-user-url] authn-session-user)) (ltu/body->edn) (ltu/is-status 200) - (ltu/get-op-url :switch-group))] + (ltu/get-op-url :switch-group)) + event-authn-info {:user-id user-id + :active-claim user-id + :claims ["group/nuvla-anon" "group/nuvla-user" session-user-id user-id]}] (testing "User cannot switch to a group that he is not part of." (-> (apply request session-json @@ -336,7 +366,15 @@ :request-method :post] authn-session-user)) (ltu/body->edn) (ltu/is-status 403) - (ltu/message-matches #"Switch group cannot be done to requested group:.*"))) + (ltu/message-matches #"Switch group cannot be done to requested group:.*")) + + (ltu/is-last-event session-user-id {:name "session.switch-group" + :description "Switch group attempt failed." + :category "action" + :success false + :linked-identifiers [group-b] + :authn-info event-authn-info + :acl {:owners ["group/nuvla-admin" group-b]}})) (testing "User can switch to a group that he is part of." (-> session-admin diff --git a/code/test/sixsq/nuvla/server/resources/spec/event_test.cljc b/code/test/sixsq/nuvla/server/resources/spec/event_test.cljc index 67a69ddda..5b7e0b001 100644 --- a/code/test/sixsq/nuvla/server/resources/spec/event_test.cljc +++ b/code/test/sixsq/nuvla/server/resources/spec/event_test.cljc @@ -11,6 +11,8 @@ (def valid-event {:id "event/262626262626262" + :name "test" + :success true :resource-type t/resource-type :created event-timestamp :updated event-timestamp @@ -21,14 +23,15 @@ :content {:resource {:href "module/HNSciCloud-RHEA/S3"} :state "Started"} :category "state" - :severity "critical"}) + :severity "critical" + :authn-info {:user-id "user/a978c1c0-f958-4238-9eba-aab85714b114" + :claims ["group/nuvla-anon" "group/nuvla-user"] + :active-claim "group/nuvla-user"}}) (deftest check-reference (let [updated-event (assoc-in valid-event [:content :resource :href] "another/valid-identifier")] - (stu/is-valid ::event/schema updated-event)) - (let [updated-event (assoc-in valid-event [:content :resource :href] "/not a valid reference/")] - (stu/is-invalid ::event/schema updated-event))) + (stu/is-valid ::event/schema updated-event))) (deftest check-severity