Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions code/src/sixsq/nuvla/auth/password.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]))


Expand Down Expand Up @@ -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)))

Expand Down
15 changes: 15 additions & 0 deletions code/src/sixsq/nuvla/server/app/routes.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)))))
Expand All @@ -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)))))
Expand All @@ -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))))


Expand Down Expand Up @@ -76,3 +90,4 @@
(route/resources (str p/service-context "static"))]
(dyn/resource-routes)
final-routes))))

4 changes: 3 additions & 1 deletion code/src/sixsq/nuvla/server/app/server.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down Expand Up @@ -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")
Expand Down
17 changes: 17 additions & 0 deletions code/src/sixsq/nuvla/server/middleware/eventer.clj
Original file line number Diff line number Diff line change
@@ -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))))
10 changes: 10 additions & 0 deletions code/src/sixsq/nuvla/server/resources/common/crud.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
82 changes: 82 additions & 0 deletions code/src/sixsq/nuvla/server/resources/common/event_config.clj
Original file line number Diff line number Diff line change
@@ -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.")))

40 changes: 40 additions & 0 deletions code/src/sixsq/nuvla/server/resources/common/event_context.clj
Original file line number Diff line number Diff line change
@@ -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)))
24 changes: 24 additions & 0 deletions code/src/sixsq/nuvla/server/resources/common/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
28 changes: 24 additions & 4 deletions code/src/sixsq/nuvla/server/resources/credential.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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)
Loading