From 8c2a4969a624bfc5cc5ba9889c20cc74fbe67396 Mon Sep 17 00:00:00 2001 From: daniel-zamora Date: Fri, 26 May 2017 14:28:29 -0400 Subject: [PATCH] CMR-3858 Update Catalog Item ACL endpoints to accept concept_id (#82) * CMR-3858 adds concept-ids to catalog item acls * CMR-3858 fixes db migration where clause * CMR-3858 fixes db migration * CMR-3858 fixes alignment * CMR-3858 fixes db migration * CMR-3858 adds some tests, corrects schema * CMR-3858 adds more test coverage * CMR-3858 adds more test coverage * CMR-3858 lauren's pr comments * CMR-3858 simplifies regex in validation, simplifies doseq in event handler * CMR-3858 santizes entry titles * CMR-3858 santizes entry titles * CMR-3858 adds doc string * CMR-3858 converting added tests to are3 * CMR-3858 converting added tests to are3 --- access-control-app/docs/acl-schema.md | 4 ++++ access-control-app/docs/acl-usage.md | 2 +- .../data/access_control_index.clj | 8 +++++++ .../cmr/access_control/data/acl_schema.clj | 2 ++ .../services/acl_validation.clj | 14 +++++++++++ .../access_control/services/event_handler.clj | 24 +++++++++++-------- 6 files changed, 43 insertions(+), 11 deletions(-) diff --git a/access-control-app/docs/acl-schema.md b/access-control-app/docs/acl-schema.md index 3cff8cb..b10623b 100644 --- a/access-control-app/docs/acl-schema.md +++ b/access-control-app/docs/acl-schema.md @@ -131,6 +131,10 @@ Properties of the `CollectionIdentifierType` object: The object is an array with all elements of the type `string`. +#### `concept-ids` (array) + +The object is an array with all elements of the type `string`. + #### `access_value` (AccessValueType) #### `temporal` (TemporalIdentifierType) diff --git a/access-control-app/docs/acl-usage.md b/access-control-app/docs/acl-usage.md index a5bd7d4..72d4d82 100644 --- a/access-control-app/docs/acl-usage.md +++ b/access-control-app/docs/acl-usage.md @@ -34,7 +34,7 @@ Unlike the other types of resource identities, catalog item identities contain a - **provider_id**: The provider with which the identity is associated - **collection_applicable**: Flag indicating whether the filter matches collections - **granule_applicable**: Flag indicating whether the filter matches granules -- **collection_identifier**: A filter defining the collections matched by this ACL. This filter consists of a combination of Collection Entry Titles, a restriction flag(AKA Access Value) range, and a temporal range (see the [schema](acl-schema.html#-collectionidentifiertype-object-)) +- **collection_identifier**: A filter defining the collections matched by this ACL. This filter consists of a combination of Collection Entry Titles, Collection Concept Ids, a restriction flag(AKA Access Value) range, and a temporal range (see the [schema](acl-schema.html#-collectionidentifiertype-object-)) - **granule_identifier**: A filter defining the granules matched by this ACL. This filter consists of a combination of restriction flag (AKA Access Value) range, and a temporal range and can be combined with a collection_identifier (see the [schema](acl-schema.html#-granuleidentifiertype-object-)) It should be noted that while temporal Catalog item filters are supported by the API, they are not currently used operationally. In addition, a restriction flag (or Access Value) filter may specify a include_undefined_value flag. If set to false, only items which have an access value within the specified range will be matched. If set to true, item with no value set, as well as those with a value in the specified range will be matched. include_undefined_value defaults to 'false' diff --git a/access-control-app/src/cmr/access_control/data/access_control_index.clj b/access-control-app/src/cmr/access_control/data/access_control_index.clj index 8df889d..2dadff0 100644 --- a/access-control-app/src/cmr/access_control/data/access_control_index.clj +++ b/access-control-app/src/cmr/access_control/data/access_control_index.clj @@ -160,6 +160,7 @@ :granule-identifier m/bool-field-mapping :granule-applicable m/bool-field-mapping + :concept-ids m/string-field-mapping :entry-title m/string-field-mapping :collection-access-value-min m/int-field-mapping @@ -316,6 +317,12 @@ (when-let [entry-titles (get-in acl [:catalog-item-identity :collection-identifier :entry-titles])] {:entry-title entry-titles})) +(defn- concept-ids-elastic-doc-map + "Returns map for entry titles to be merged into full elastic doc" + [acl] + (when-let [concept-ids (get-in acl [:catalog-item-identity :collection-identifier :concept-ids])] + {:concept-ids concept-ids})) + (defn acl-concept-map->elastic-doc "Converts a concept map containing an acl into the elasticsearch document to index." [concept-map] @@ -334,6 +341,7 @@ (access-value-elastic-doc-map acl) (temporal-elastic-doc-map acl) (entry-title-elastic-doc-map acl) + (concept-ids-elastic-doc-map acl) (identifier-applicable-elastic-doc-map acl) (assoc (select-keys concept-map [:concept-id :revision-id]) :display-name display-name diff --git a/access-control-app/src/cmr/access_control/data/acl_schema.clj b/access-control-app/src/cmr/access_control/data/acl_schema.clj index cd69cd9..6944431 100644 --- a/access-control-app/src/cmr/access_control/data/acl_schema.clj +++ b/access-control-app/src/cmr/access_control/data/acl_schema.clj @@ -149,6 +149,8 @@ :properties {:entry_titles {:type :array :items {:type :string :minLength 1}} + :concept_ids {:type :array + :items (ref-def :IdentifierType)} :access_value (ref-def :AccessValueType) :temporal (ref-def :TemporalIdentifierType)}} :GranuleIdentifierType {:type :object diff --git a/access-control-app/src/cmr/access_control/services/acl_validation.clj b/access-control-app/src/cmr/access_control/services/acl_validation.clj index 1adab70..5cc42e4 100644 --- a/access-control-app/src/cmr/access_control/services/acl_validation.clj +++ b/access-control-app/src/cmr/access_control/services/acl_validation.clj @@ -7,6 +7,7 @@ [cmr.acl.core :as acl] [cmr.common.date-time-parser :as dtp] [cmr.common.validations.core :as v] + [cmr.common.concepts :as concepts] [cmr.transmit.metadata-db :as mdb1] [cmr.transmit.metadata-db2 :as mdb])) @@ -93,6 +94,18 @@ (when-not (seq (mdb1/find-concepts context {:provider-id provider-id :entry-title entry-title} :collection)) {key-path [(format "collection with entry-title [%s] does not exist in provider [%s]" entry-title provider-id)]}))))) +(defn- make-collection-concept-ids-validations + "Returns a validation for the concept_ids part of a collection identifier, closed over the context and ACL to be validated." + [context acl] + (let [provider-id (-> acl :catalog-item-identity :provider-id)] + [(v/every (fn [key-path concept-id] + (let [regex #"C\d+-\S+"] + (when-not (re-matches regex concept-id) + {key-path [(format "[%s] is not a valid collection concept-id." concept-id)]})))) + (v/every (fn [key-path concept-id] + (when-not (seq (mdb1/find-concepts context {:provider-id provider-id :concept-id concept-id} :collection)) + {key-path [(format "collection with concept-id [%s] does not exist in provider [%s]" concept-id provider-id)]})))])) + (defn- access-value-validation "Validates the access_value part of a collection or granule identifier." [key-path access-value-map] @@ -121,6 +134,7 @@ "Returns a validation for an ACL catalog_item_identity.collection_identifier closed over the given context and ACL to be validated." [context acl] {:entry-titles (v/when-present (make-collection-entry-titles-validation context acl)) + :concept-ids (v/when-present (make-collection-concept-ids-validations context acl)) :access-value (v/when-present [access-value-validation access-value-min-max-value-validation]) :temporal (v/when-present temporal-identifier-validation)}) diff --git a/access-control-app/src/cmr/access_control/services/event_handler.clj b/access-control-app/src/cmr/access_control/services/event_handler.clj index ea6784f..9cafb46 100644 --- a/access-control-app/src/cmr/access_control/services/event_handler.clj +++ b/access-control-app/src/cmr/access_control/services/event_handler.clj @@ -80,22 +80,26 @@ (defmethod handle-indexing-event [:concept-delete :collection] [context {:keys [concept-id revision-id]}] (let [concept-map (mdb/get-concept context concept-id revision-id) - collection-concept (acl-matchers/add-acl-enforcement-fields-to-concept concept-map) - entry-title (:EntryTitle collection-concept)] - (doseq [acl-concept (acl-service/get-all-acl-concepts context) + collection-concept (acl-matchers/add-acl-enforcement-fields-to-concept concept-map)] + (doseq [key-path [:entry-titles :concept-ids] + acl-concept (acl-service/get-all-acl-concepts context) :let [parsed-acl (acl-service/get-parsed-acl acl-concept) catalog-item-id (:catalog-item-identity parsed-acl) - acl-entry-titles (:entry-titles (:collection-identifier catalog-item-id))] + value (if (= key-path :entry-titles) + (:EntryTitle collection-concept) + (:concept-id collection-concept)) + acl-values (get (:collection-identifier catalog-item-id) key-path)] :when (and (= (:provider-id collection-concept) (:provider-id catalog-item-id)) - (some #{entry-title} acl-entry-titles))] - (if (= 1 (count acl-entry-titles)) - ;; the ACL only references the collection being deleted, and therefore the ACL should be deleted + (some #{value} acl-values))] + (if (= 1 (count acl-values)) + ;; The ACL only references the collection being deleted, and therefore the ACL should be deleted. + ;; With the addition of concept-ids, this assumes entry-titles and concept-ids are in sync. (acl-service/delete-acl (transmit-config/with-echo-system-token context) (:concept-id acl-concept)) - ;; otherwise the ACL references other collections, and will be updated + ;; Otherwise the ACL references other collections, and will be updated (let [new-acl (update-in parsed-acl - [:catalog-item-identity :collection-identifier :entry-titles] - #(remove #{entry-title} %))] + [:catalog-item-identity :collection-identifier key-path] + #(remove #{value} %))] (acl-service/update-acl (transmit-config/with-echo-system-token context) (:concept-id acl-concept) new-acl))))))