Repository: gisaia/ARLAS-server (language: python)

Issue:
Filter collections on organisation name
We expect to be able to filter the access to collections by the name of an organisation given as a header `arlas-organization` in requests.

A request comes in to REST services with optional HTTP headers as follows:
arlas-organization: foo (maybe a list: refered below as [ORG]; only if IAM activated)
column-filter: col1:*,col2:* (refered below as CF)

For a request containing a collection name (COL) as PathParam or QueryParam:
if COL is public then OK
else 
  if [ORG] is present, and COL.org is not in [ORG] then NOK
  else if CF is present (even empty), use it (COL must be in CF)
    else OK



Discussion / hints:

Fix patch:
diff --git a/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java b/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java
index 64b1fb5ab..8a30a6265 100644
--- a/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java
+++ b/arlas-admin/src/main/java/io/arlas/server/admin/task/CollectionAutoDiscover.java
@@ -54,7 +54,7 @@ public void execute(Map<String,List<String>> arg0, PrintWriter arg1) throws Exce
             List<CollectionReferenceDescription> discoveredCollections = collectionReferenceService.getAllIndicesAsCollections();
             List<CollectionReferenceDescription> existingCollections;
             try {
-                existingCollections = collectionReferenceService.describeAllCollections(collectionReferenceService.getAllCollectionReferences(Optional.empty()), Optional.empty());
+                existingCollections = collectionReferenceService.describeAllCollections(collectionReferenceService.getAllCollectionReferences(Optional.empty(), Optional.empty()), Optional.empty());
             } catch (Exception e) {
                 existingCollections = new ArrayList<>();
             }
diff --git a/arlas-commons/src/main/java/io/arlas/commons/config/ArlasAuthConfiguration.java b/arlas-commons/src/main/java/io/arlas/commons/config/ArlasAuthConfiguration.java
index 9599e36f4..0df9d0731 100644
--- a/arlas-commons/src/main/java/io/arlas/commons/config/ArlasAuthConfiguration.java
+++ b/arlas-commons/src/main/java/io/arlas/commons/config/ArlasAuthConfiguration.java
@@ -24,7 +24,6 @@
 import io.arlas.filter.config.InitConfiguration;
 import org.keycloak.representations.adapters.config.AdapterConfig;
 
-import javax.validation.constraints.NotNull;
 import javax.ws.rs.HttpMethod;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -43,10 +42,6 @@ public class ArlasAuthConfiguration {
     @JsonProperty("header_group")
     public String headerGroup;
 
-    @NotNull
-    @JsonProperty("header_organisation")
-    public String headerOrganisation;
-
     @JsonProperty("anonymous_value")
     public String anonymousValue;
 
diff --git a/arlas-commons/src/main/java/io/arlas/commons/rest/utils/ServerConstants.java b/arlas-commons/src/main/java/io/arlas/commons/rest/utils/ServerConstants.java
index 2cb2e70f4..ee4b81c28 100644
--- a/arlas-commons/src/main/java/io/arlas/commons/rest/utils/ServerConstants.java
+++ b/arlas-commons/src/main/java/io/arlas/commons/rest/utils/ServerConstants.java
@@ -22,6 +22,7 @@
 public final class ServerConstants {
     public static final String COLUMN_FILTER = "column-filter";
     public static final String PARTITION_FILTER = "partition-filter";
+    public static final String ARLAS_ORGANISATION = "arlas-organization";
 
     private ServerConstants() { }
 }
diff --git a/arlas-commons/src/main/java/io/arlas/filter/core/IdentityParam.java b/arlas-commons/src/main/java/io/arlas/filter/core/IdentityParam.java
index 12d278306..260a61d65 100644
--- a/arlas-commons/src/main/java/io/arlas/filter/core/IdentityParam.java
+++ b/arlas-commons/src/main/java/io/arlas/filter/core/IdentityParam.java
@@ -27,6 +27,8 @@
 import java.util.List;
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
+
 public class IdentityParam {
 
     public String userId;
@@ -38,7 +40,7 @@ public IdentityParam(ArlasAuthConfiguration configuration, HttpHeaders headers)
                 .orElse(configuration.anonymousValue);
 
         // in a context where resources are publicly available, no organisation is defined
-        this.organisation = Optional.ofNullable(headers.getHeaderString(configuration.headerOrganisation))
+        this.organisation = Optional.ofNullable(headers.getHeaderString(ARLAS_ORGANISATION))
                 .orElse(PolicyEnforcer.NO_ORG);
 
         this.groups = Arrays.stream(Optional.ofNullable(headers.getHeaderString(configuration.headerGroup))
diff --git a/arlas-core/src/main/java/io/arlas/server/core/app/ArlasServerConfiguration.java b/arlas-core/src/main/java/io/arlas/server/core/app/ArlasServerConfiguration.java
index 73e5f5a83..05aa1225d 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/app/ArlasServerConfiguration.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/app/ArlasServerConfiguration.java
@@ -81,10 +81,6 @@ public class ArlasServerConfiguration extends ArlasBaseConfiguration {
     @JsonProperty("collection-auto-discover")
     public CollectionAutoDiscoverConfiguration collectionAutoDiscoverConfiguration;
 
-    @JsonProperty("arlas_default_org")
-    public String arlasDefaultOrg;
-
-
     public void check() throws ArlasConfigurationException {
         super.check();
 
diff --git a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticCollectionReferenceService.java b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticCollectionReferenceService.java
index a400896b4..27e2afb40 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticCollectionReferenceService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/impl/elastic/services/ElasticCollectionReferenceService.java
@@ -23,6 +23,7 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectReader;
 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import io.arlas.server.core.exceptions.CollectionUnavailableException;
 import io.arlas.server.core.services.CollectionReferenceService;
 import io.arlas.commons.exceptions.ArlasException;
 import io.arlas.commons.exceptions.InternalServerErrorException;
@@ -101,7 +102,7 @@ protected Map<String, LinkedHashMap> getAllMappingsFromDao(String arlasIndex) th
     }
 
     @Override
-    public List<CollectionReference> getAllCollectionReferences(Optional<String> columnFilter) throws ArlasException {
+    public List<CollectionReference> getAllCollectionReferences(Optional<String> columnFilter, Optional<String> organisations) throws ArlasException {
         List<CollectionReference> collections = new ArrayList<>();
 
         try {
@@ -120,15 +121,20 @@ public List<CollectionReference> getAllCollectionReferences(Optional<String> col
                 for (SearchHit hit : scrollResp.getHits().getHits()) {
                     String source = hit.getSourceAsString();
                     try {
+                        CollectionReference colRef = new CollectionReference(hit.getId(), reader.readValue(source));
+                        checkIfAllowedForOrganisations(colRef, organisations);
                         for (String c : allowedCollections) {
                             if ((c.endsWith("*") && hit.getId().startsWith(c.substring(0, c.indexOf("*"))))
-                                    || hit.getId().equals(c)){
-                                collections.add(new CollectionReference(hit.getId(), reader.readValue(source)));
+                                    || hit.getId().equals(c)) {
+                                collections.add(colRef);
                                 break;
                             }
                         }
                     } catch (IOException e) {
                         throw new InternalServerErrorException("Can not fetch collection", e);
+                    } catch (CollectionUnavailableException e) {
+                        LOGGER.warn(String.format("Collection %s not available for this organisation %s",
+                                hit.getId(), organisations));
                     }
                 }
                 SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollResp.getScrollId());
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionDisplayNames.java b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionDisplayNames.java
index c2107cc43..ef6e8a411 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionDisplayNames.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionDisplayNames.java
@@ -24,13 +24,16 @@
 import java.io.Serializable;
 import java.util.Map;
 
+import static io.arlas.server.core.model.CollectionReference.COLLECTION_DISPLAY_NAME;
+import static io.arlas.server.core.model.CollectionReference.FIELD_DISPLAY_NAME;
+
 public class CollectionDisplayNames implements Serializable {
     private static final long serialVersionUID = 7438255714694047836L;
 
-    @JsonProperty(value = "collection", required = false)
+    @JsonProperty(value = COLLECTION_DISPLAY_NAME, required = false)
     public String collection = null;
 
-    @JsonProperty(value = "fields", required = false)
+    @JsonProperty(value = FIELD_DISPLAY_NAME, required = false)
     public Map<String, String> fields = null;
 
     @JsonProperty(value = "shape_columns", required = false)
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionOrganisations.java b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionOrganisations.java
new file mode 100644
index 000000000..ee575a526
--- /dev/null
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionOrganisations.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to Gisaïa under one or more contributor
+ * license agreements. See the NOTICE.txt file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Gisaïa licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.arlas.server.core.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+import static io.arlas.server.core.model.CollectionReference.*;
+
+public class CollectionOrganisations implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 4330989126306933505L;
+
+    @JsonProperty(value = ORGANISATIONS_OWNER, required = false)
+    public String owner = null;
+
+    @JsonProperty(value = ORGANISATIONS_SHARED, required = false)
+    public List<String> sharedWith = null;
+
+    @JsonProperty(value = ORGANISATIONS_PUBLIC, required = false)
+    public Boolean isPublic = false;
+
+}
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java
index e02882d26..c95e62762 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReference.java
@@ -40,6 +40,10 @@ public class CollectionReference implements Serializable {
     public static final String DISPLAY_NAMES = "display_names";
     public static final String COLLECTION_DISPLAY_NAME = "collection";
     public static final String FIELD_DISPLAY_NAME = "fields";
+    public static final String ORGANISATIONS = "organisations";
+    public static final String ORGANISATIONS_OWNER = "owner";
+    public static final String ORGANISATIONS_SHARED = "shared";
+    public static final String ORGANISATIONS_PUBLIC = "public";
     public static final String INSPIRE_PATH = "inspire";
     public static final String DUBLIN_CORE_PATH = "dublin_core_element_name";
     public static final String INSPIRE_LINEAGE = "lineage";
diff --git a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java
index 6c5371884..bbf625473 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/model/CollectionReferenceParameters.java
@@ -76,6 +76,9 @@ public class CollectionReferenceParameters implements Serializable {
     @JsonProperty(value = CollectionReference.DISPLAY_NAMES)
     public CollectionDisplayNames collectionDisplayNames = null;
 
+    @JsonProperty(value = CollectionReference.ORGANISATIONS)
+    public CollectionOrganisations collectionOrganisations = null;
+
     @JsonProperty(value = "atom_feed")
     public Feed atomFeed = null;
 
diff --git a/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java b/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java
index 585453533..3e5451f63 100644
--- a/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java
+++ b/arlas-core/src/main/java/io/arlas/server/core/services/CollectionReferenceService.java
@@ -22,6 +22,7 @@
 import io.arlas.commons.exceptions.ArlasException;
 import io.arlas.commons.exceptions.NotFoundException;
 import io.arlas.commons.utils.StringUtil;
+import io.arlas.server.core.exceptions.CollectionUnavailableException;
 import io.arlas.server.core.managers.CacheManager;
 import io.arlas.server.core.model.CollectionReference;
 import io.arlas.server.core.model.CollectionReferenceParameters;
@@ -61,18 +62,19 @@ public CollectionReferenceService(String arlasIndex, CacheManager cacheManager)
 
     abstract public void initCollectionDatabase() throws ArlasException;
 
-    abstract public List<CollectionReference> getAllCollectionReferences(Optional<String> columnFilter) throws ArlasException;
+    abstract public List<CollectionReference> getAllCollectionReferences(Optional<String> columnFilter, Optional<String> organisations) throws ArlasException;
 
     abstract public void deleteCollectionReference(String ref) throws ArlasException;
 
     // -------
 
-    public CollectionReference getCollectionReference(String ref) throws ArlasException {
+    public CollectionReference getCollectionReference(String ref, Optional<String> organisations) throws ArlasException {
         CollectionReference collectionReference = cacheManager.getCollectionReference(ref);
         if (collectionReference == null) {
             collectionReference = getCollectionReferenceFromDao(ref);
             cacheManager.putCollectionReference(ref, collectionReference);
         }
+        checkIfAllowedForOrganisations(collectionReference, organisations);
         if (!getMapping(collectionReference.params.indexName).isEmpty()){
             return collectionReference;
         } else {
@@ -354,4 +356,43 @@ private FieldType getFieldType(String field, String index) throws ArlasException
         return ret.get();
     }
 
+    protected void checkIfAllowedForOrganisations(CollectionReference collection,
+                                               Optional<String> organisations)
+            throws CollectionUnavailableException {
+        checkIfAllowedForOrganisations(collection, organisations, false);
+    }
+
+    public void checkIfAllowedForOrganisations(CollectionReference collection,
+                                               Optional<String> organisations,
+                                               boolean ownerOnly)
+            throws CollectionUnavailableException {
+        if (organisations.isEmpty()) {
+            // no header, we'll trust the column filter if any
+            LOGGER.debug("No organisation header");
+            return;
+        }
+
+        if (!ownerOnly &&
+                (collection.params.collectionOrganisations == null ||
+                        collection.params.collectionOrganisations.isPublic ||
+                        collection.params.collectionOrganisations.owner == null)) {
+            // do we consider a collection with no organisation attribute open to all?
+            LOGGER.debug(String.format("Collection %s organisation is public or null: %s",
+                    collection.collectionName, collection.params.collectionOrganisations));
+            return;
+        }
+
+        List<String> o = new ArrayList<>();
+        o.add(collection.params.collectionOrganisations.owner);
+        if (!ownerOnly && collection.params.collectionOrganisations.sharedWith != null) {
+            o.addAll(collection.params.collectionOrganisations.sharedWith);
+        }
+        LOGGER.debug("collection's organisations=" + o);
+        LOGGER.debug("header=" + organisations.get());
+        o.retainAll(Arrays.stream(organisations.get().split(",")).toList());
+        LOGGER.debug("allowed org=" + o);
+        if (o.isEmpty()) {
+            throw new CollectionUnavailableException("The collection not available with organisation header: " + organisations.get());
+        }
+    }
 }
diff --git a/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/AtomHitsMessageBodyWriter.java b/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/AtomHitsMessageBodyWriter.java
index 82e4d5f2a..b0e70ef01 100644
--- a/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/AtomHitsMessageBodyWriter.java
+++ b/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/AtomHitsMessageBodyWriter.java
@@ -95,7 +95,7 @@ public void writeTo(Hits hits, Class aClass, Type type, Annotation[] annotations
 
             CollectionReference cr;
             try {
-                cr = exploreService.getCollectionReferenceService().getCollectionReference(hits.collection);
+                cr = exploreService.getCollectionReferenceService().getCollectionReference(hits.collection, Optional.empty());
             } catch (ArlasException e) {
                 throw new WebApplicationException("Can not access collection metadata", e);
             }
diff --git a/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/OpenSearchDescriptorService.java b/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/OpenSearchDescriptorService.java
index b5651851c..ca7d183c1 100644
--- a/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/OpenSearchDescriptorService.java
+++ b/arlas-opensearch/src/main/java/io/arlas/server/opensearch/rest/explore/OpenSearchDescriptorService.java
@@ -47,6 +47,7 @@
 import java.io.IOException;
 import java.util.*;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 
 public class OpenSearchDescriptorService extends ExploreRESTServices {
@@ -79,6 +80,10 @@ public Response opensearch(@Context UriInfo uri,
             // --------------------------------------------------------
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  EXTRA   -----------------------
             // --------------------------------------------------------
@@ -86,7 +91,7 @@ public Response opensearch(@Context UriInfo uri,
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws IOException, NotFoundException, ArlasException {
         CollectionReference cr = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (cr == null) {
             throw new NotFoundException(collection);
         }
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/collections/CollectionService.java b/arlas-rest/src/main/java/io/arlas/server/rest/collections/CollectionService.java
index 7d6eb9b05..1bb4333b8 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/collections/CollectionService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/collections/CollectionService.java
@@ -52,6 +52,7 @@
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 
 public class CollectionService extends CollectionRESTServices {
@@ -84,6 +85,9 @@ public Response getAll(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -91,7 +95,7 @@ public Response getAll(
                     defaultValue = "false")
             @QueryParam(value = "pretty") Boolean pretty
     ) throws ArlasException {
-        List<CollectionReference> collections = collectionReferenceService.getAllCollectionReferences(columnFilter);
+        List<CollectionReference> collections = collectionReferenceService.getAllCollectionReferences(columnFilter, organisations);
         return ResponseFormatter.getResultResponse(collections);
     }
 
@@ -111,9 +115,12 @@ public Response getAll(
 
     public Response exportCollections(
             @ApiParam(hidden = true)
-            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
+            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
             ) throws ArlasException {
-        List<CollectionReference> collections = collectionReferenceService.getAllCollectionReferences(columnFilter);
+        List<CollectionReference> collections = collectionReferenceService.getAllCollectionReferences(columnFilter, organisations);
         String date = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
         String fileName = "arlas-collections-export_" + date + ".json";
         removeMetacollection(collections);
@@ -136,6 +143,10 @@ public Response exportCollections(
     public Response importCollections(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             @FormDataParam("file") InputStream inputStream,
             @FormDataParam("file") FormDataContentDisposition fileDetail
     ) throws ArlasException {
@@ -144,13 +155,14 @@ public Response importCollections(
         removeMetacollection(collections);
         Set<String> allowedCollections = ColumnFilterUtil.getAllowedCollections(columnFilter);
         for (CollectionReference collection : collections) {
+            collectionReferenceService.checkIfAllowedForOrganisations(collection, organisations, true);
             for (String c : allowedCollections) {
                 if ((c.endsWith("*") && collection.collectionName.startsWith(c.substring(0, c.indexOf("*"))))
-                        || collection.collectionName.equals(c)){
+                        || collection.collectionName.equals(c)) {
                     try {
                         savedCollections.add(save(collection.collectionName, collection.params, true));
                     } catch (Exception e) {
-                            throw new ArlasException(e.getMessage());
+                        throw new ArlasException(e.getMessage());
                     }
                 } else {
                     throw new CollectionUnavailableException("Collection '" + collection.collectionName + "' not authorized by column filter");
@@ -194,6 +206,12 @@ public Response get(
                     required = true)
             @PathParam(value = "collection") String collection,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -202,7 +220,8 @@ public Response get(
                     defaultValue = "false")
             @QueryParam(value = "pretty") Boolean pretty
     ) throws ArlasException {
-        CollectionReference cr = collectionReferenceService.getCollectionReference(collection);
+        CollectionReference cr = collectionReferenceService.getCollectionReference(collection, organisations);
+        ColumnFilterUtil.assertCollectionsAllowed(columnFilter, List.of(cr));
         return ResponseFormatter.getResultResponse(cr);
     }
 
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java
index 255ad90d9..13fd390f2 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/AggregateRESTService.java
@@ -45,8 +45,7 @@
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class AggregateRESTService extends ExploreRESTServices {
 
@@ -110,6 +109,9 @@ public Response aggregate(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -128,7 +130,7 @@ public Response aggregate(
     ) throws ArlasException {
         long startArlasTime = System.nanoTime();
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -184,6 +186,9 @@ public Response aggregatePost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -200,7 +205,7 @@ public Response aggregatePost(
     ) throws NotFoundException, ArlasException {
         long startArlasTime = System.nanoTime();
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
 
         if (collectionReference == null) {
             throw new NotFoundException(collection);
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
index 6201cc6c7..515a758f9 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/aggregate/GeoAggregateRESTService.java
@@ -52,8 +52,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class GeoAggregateRESTService extends ExploreRESTServices {
 
@@ -122,6 +121,9 @@ public Response geoaggregate(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -141,7 +143,7 @@ public Response geoaggregate(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -207,6 +209,9 @@ public Response shapeaggregate(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -222,7 +227,7 @@ public Response shapeaggregate(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -290,6 +295,9 @@ public Response geohashgeoaggregate(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM ---------------------------
             // --------------------------------------------------------
@@ -310,7 +318,7 @@ public Response geohashgeoaggregate(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -420,6 +428,9 @@ public Response geotilegeoaggregate(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM ---------------------------
             // --------------------------------------------------------
@@ -440,7 +451,7 @@ public Response geotilegeoaggregate(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -488,7 +499,7 @@ private List<BoundingBox> getBoundingBoxes(String geohash, List<String> agg, Col
         List<Interval> intervals = ParamsParser.getAggregations(collectionReference, agg).stream()
                 .filter(a -> a.type.equals(AggregationTypeEnum.geohash))
                 .map(a -> a.interval)
-                .collect(Collectors.toList());
+                .toList();
         if (intervals.size() > 0) {
             interval = intervals.get(0).value.intValue();
         }
@@ -515,7 +526,7 @@ private List<BoundingBox> getBoundingBoxes(Integer z, Integer x, Integer y, List
         List<Interval> intervals = ParamsParser.getAggregations(collectionReference, agg).stream()
                 .filter(a -> a.type.equals(AggregationTypeEnum.geotile))
                 .map(a -> a.interval)
-                .collect(Collectors.toList());
+                .toList();
         if (intervals.size() > 0) {
             interval = intervals.get(0).value.intValue();
             if (interval - z > 7 || interval - z < 0) {
@@ -588,6 +599,9 @@ public Response geoaggregatePost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -603,7 +617,7 @@ public Response geoaggregatePost(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -658,6 +672,9 @@ public Response shapeaggregatePost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -673,7 +690,7 @@ public Response shapeaggregatePost(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java
index f11e6dfd4..c42c61dd9 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/compute/ComputeRESTService.java
@@ -42,8 +42,7 @@
 import java.util.List;
 import java.util.Optional;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class ComputeRESTService extends ExploreRESTServices {
 
@@ -114,6 +113,9 @@ public Response compute(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -129,7 +131,7 @@ public Response compute(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -186,6 +188,9 @@ public Response computePost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -201,7 +206,7 @@ public Response computePost(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java
index 7519104d0..ffb93537e 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/count/CountRESTService.java
@@ -41,8 +41,7 @@
 import java.util.List;
 import java.util.Optional;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class CountRESTService extends ExploreRESTServices {
 
@@ -94,6 +93,9 @@ public Response count(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -108,7 +110,7 @@ public Response count(
             @ApiParam(value = "max-age-cache")
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
-        CollectionReference collectionReference = exploreService.getCollectionReferenceService().getCollectionReference(collection);
+        CollectionReference collectionReference = exploreService.getCollectionReferenceService().getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -158,6 +160,9 @@ public Response countPost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -171,7 +176,7 @@ public Response countPost(
             // --------------------------------------------------------
             Count count
     ) throws NotFoundException, ArlasException {
-        CollectionReference collectionReference = exploreService.getCollectionReferenceService().getCollectionReference(collection);
+        CollectionReference collectionReference = exploreService.getCollectionReferenceService().getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeCollectionRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeCollectionRESTService.java
index e0a43ad89..33709cfe5 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeCollectionRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeCollectionRESTService.java
@@ -37,6 +37,7 @@
 import java.util.Collections;
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 
 public class DescribeCollectionRESTService extends ExploreRESTServices {
@@ -66,6 +67,9 @@ public Response describe(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -82,7 +86,7 @@ public Response describe(
     ) throws ArlasException {
 
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
 
         if (collectionReference == null) {
             throw new NotFoundException(collection);
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeRESTService.java
index e5866f4b6..f86b39a61 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/describe/DescribeRESTService.java
@@ -37,6 +37,7 @@
 import java.util.List;
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 
 public class DescribeRESTService extends ExploreRESTServices {
@@ -58,6 +59,8 @@ public Response list(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
 
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
@@ -73,7 +76,7 @@ public Response list(
             @ApiParam(value = "max-age-cache")
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws ArlasException {
-        List<CollectionReference> collectionReferences = exploreService.getCollectionReferenceService().getAllCollectionReferences(columnFilter);
+        List<CollectionReference> collectionReferences = exploreService.getCollectionReferenceService().getAllCollectionReferences(columnFilter, organisations);
         List<CollectionReferenceDescription> collectionReferenceDescriptionList = exploreService.describeAllCollections(collectionReferences, columnFilter);
 
         return cache(Response.ok(collectionReferenceDescriptionList), maxagecache);
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/raw/RawRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/raw/RawRESTService.java
index 7bbc7b8c2..594bc35b6 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/raw/RawRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/raw/RawRESTService.java
@@ -40,6 +40,7 @@
 import java.util.Map;
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 
 public class RawRESTService extends ExploreRESTServices {
@@ -92,6 +93,9 @@ public Response getArlasHit(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- EXTRA -----------------------
             // --------------------------------------------------------
@@ -99,7 +103,7 @@ public Response getArlasHit(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException("Collection " + collection + " not found.");
         }
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java
index 689a406f1..e9e8ef2b5 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/GeoSearchRESTService.java
@@ -48,8 +48,7 @@
 import java.util.List;
 import java.util.Optional;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class GeoSearchRESTService extends ExploreRESTServices {
 
@@ -100,6 +99,9 @@ public Response geosearch(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -174,7 +176,7 @@ public Response geosearch(
     ) throws NotFoundException, ArlasException {
 
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -227,6 +229,9 @@ public Response shapesearch(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -296,7 +301,7 @@ public Response shapesearch(
     ) throws NotFoundException, ArlasException {
 
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -362,6 +367,9 @@ public Response tiledgeosearch(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -435,7 +443,7 @@ public Response tiledgeosearch(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -470,7 +478,7 @@ public Response geosearchPost(
             @PathParam(value = "collection") String collection,
 
             // --------------------------------------------------------
-            // -----------------------  Search   -----------------------
+            // -----------------------  Search   ----------------------
             // --------------------------------------------------------
             Search search,
 
@@ -484,6 +492,9 @@ public Response geosearchPost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -498,7 +509,7 @@ public Response geosearchPost(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -557,6 +568,9 @@ public Response shapesearchPost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -571,7 +585,7 @@ public Response shapesearchPost(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -643,6 +657,7 @@ private Response geosearch(CollectionReference collectionReference, Filter filte
                 try {
                     FileUtils.forceDeleteOnExit(result);
                 } catch (IOException e) {
+                    //ignored
                 }
             }
         } else {
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java
index 022348f94..abfd7090d 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/explore/search/SearchRESTService.java
@@ -46,8 +46,7 @@
 import java.util.List;
 import java.util.Optional;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class SearchRESTService extends ExploreRESTServices {
 
@@ -101,6 +100,9 @@ public Response search(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  FORM    -----------------------
             // --------------------------------------------------------
@@ -175,7 +177,7 @@ public Response search(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
@@ -234,6 +236,9 @@ public Response searchPost(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -249,7 +254,7 @@ public Response searchPost(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
diff --git a/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java b/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java
index 9a4752410..cbedbc726 100644
--- a/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java
+++ b/arlas-rest/src/main/java/io/arlas/server/rest/plugins/eo/TileRESTService.java
@@ -54,8 +54,7 @@
 import java.util.Queue;
 import java.util.stream.Collectors;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class TileRESTService extends ExploreRESTServices {
     public final static String PRODUCES_PNG =  "image/png";
@@ -119,6 +118,9 @@ public Response tiledgeosearch(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
+
             // --------------------------------------------------------
             // -----------------------  PAGE    -----------------------
             // --------------------------------------------------------
@@ -171,7 +173,7 @@ public Response tiledgeosearch(
             @QueryParam(value = "max-age-cache") Integer maxagecache
     ) throws NotFoundException, ArlasException {
         CollectionReference collectionReference = exploreService.getCollectionReferenceService()
-                .getCollectionReference(collection);
+                .getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new NotFoundException(collection);
         }
diff --git a/conf/configuration.yaml b/conf/configuration.yaml
index 6a3dd7dd3..01c37d7d4 100644
--- a/conf/configuration.yaml
+++ b/conf/configuration.yaml
@@ -178,7 +178,6 @@ arlas_database_factory_class: ${ARLAS_DATABASE_FACTORY_CLASS:-io.arlas.server.im
 ############ AUTHENTICATION              ###############
 ########################################################
 arlas_auth_policy_class: ${ARLAS_AUTH_POLICY_CLASS:-io.arlas.filter.impl.NoPolicyEnforcer}
-arlas_default_org: ${ARLAS_DEFAULT_ORG:-arlas}
 
 arlas_auth:
   permission_url: ${ARLAS_AUTH_PERMISSION_URL:-http://arlas-iam-server/arlas_iam_server/auth/permissions}
@@ -188,7 +187,6 @@ arlas_auth:
   certificate_url: ${ARLAS_AUTH_CERT_URL:-}
   header_user: ${ARLAS_HEADER_USER:-arlas-user}
   header_group: ${ARLAS_HEADER_GROUP:-arlas-groups}
-  header_organisation: ${ARLAS_ORGANIZATION_HEADER:-arlas-organization}
   anonymous_value: ${ARLAS_ANONYMOUS_VALUE:-anonymous}
   claim_roles: ${ARLAS_CLAIM_ROLES:-http://arlas.io/roles}
   claim_permissions: ${ARLAS_CLAIM_PERMISSIONS:-http://arlas.io/permissions}
diff --git a/docs/arlas-api-collection.md b/docs/arlas-api-collection.md
index e01162ea6..1fe7ffb0f 100644
--- a/docs/arlas-api-collection.md
+++ b/docs/arlas-api-collection.md
@@ -63,8 +63,10 @@ Examples of `column-filter`:
 - `params`, `:params`, `*:params` make available `params` and subfields for every collection
 - `*` makes all fields available
 - `*.*` makes only subfields available, e.g. `params.city` and `params.country` but not `id`
+- `` (empty value) makes all fields and collections forbidden.
 
-If no column filter, or a blank column filter is provided, then no filtering is done.
+If no column filter, then no filtering is done.
+An empty column filter will forbid all collections and columns.
 
 The following endpoints use this header:
 
diff --git a/docs/arlas-api-exploration.md b/docs/arlas-api-exploration.md
index 9783db9b9..b4d7a4a94 100644
--- a/docs/arlas-api-exploration.md
+++ b/docs/arlas-api-exploration.md
@@ -303,8 +303,10 @@ Examples of `column-filter`:
 - `params`, `:params`, `*:params` make available `params` and subfields for every collection
 - `*` makes all fields available
 - `*.*` makes only subfields available, e.g. `params.city` and `params.country` but not `id`
+- `` (empty value) makes all fields and collections forbidden.
 
-If no column filter, or a blank column filter is provided, then no filtering is done.
+If no column filter, then no filtering is done.
+An empty column filter will forbid all collections and columns.
 
 The following endpoints use this header:
 
diff --git a/docs/arlas-collection-model.md b/docs/arlas-collection-model.md
index a70327434..a1d3fb747 100644
--- a/docs/arlas-collection-model.md
+++ b/docs/arlas-collection-model.md
@@ -30,6 +30,13 @@ A Collection has the following structure :
       "fields": { "${doc_json_path}" :  "string" },
       "shape_columns": { "${geojson_flatten_path}" :  "string" }
     },
+    "organisations": {
+      "owner": "string",
+      "shared": [
+        "string"
+      ],
+      "public": "boolean"
+    },
     "atom_feed": {
       ...
     },
diff --git a/docs/arlas-ogc.md b/docs/arlas-ogc.md
index 726c1f4f2..c810c8cc0 100644
--- a/docs/arlas-ogc.md
+++ b/docs/arlas-ogc.md
@@ -233,8 +233,10 @@ Examples of `column-filter`:
 - `params`, `:params`, `*:params` make available `params` and subfields for every collection
 - `*` makes all fields available
 - `*.*` makes only subfields available, e.g. `params.city` and `params.country` but not `id`
+- `` (empty value) makes all fields and collections forbidden.
 
-If no column filter, or a blank column filter is provided, then no filtering is done.
+If no column filter, then no filtering is done.
+An empty column filter will forbid all collections and columns.
 
 The following OGC operations use this header:
 
diff --git a/docs/arlas-security.md b/docs/arlas-security.md
index 4edb99add..4cd38426d 100644
--- a/docs/arlas-security.md
+++ b/docs/arlas-security.md
@@ -80,10 +80,10 @@ Variables are injected in rules and headers.
 
 ```asciidoc
 "variable:organisation:acme",
-"header:arlas-organisation:${organisation}"
+"header:arlas-organization:${organisation}"
 ```
 
-*will inject a header `arlas-organisation:acme` in the request.*
+*will inject a header `arlas-organization:acme` in the request.*
 
 - **A list of headers** to be injected to all the requests that require a restricted access 
 (such as the partition-filter), e.g. `header:${header}:${value}`
@@ -131,3 +131,7 @@ This allows certain data to be restricted to certain users.
 !!! info "partition-filter syntax"
     See the `partition-filter` syntax in [ARLAS Exploration API configuration section](http://docs.arlas.io/arlas-tech/current/arlas-api-exploration/#column-filtering).
 
+#### Organisation filter
+If a header `arlas-organization` is present (can be empty, or be a comma separated list of values), it will be used
+to check if the collection's organisations (owner or shared with) match one of the provided values.
+If no header `arlas-organization` then no check is done on the collection's organisations.
\ No newline at end of file
diff --git a/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWRESTService.java b/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWRESTService.java
index 7a468c9a0..3539b3ffe 100644
--- a/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWRESTService.java
+++ b/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWRESTService.java
@@ -65,6 +65,7 @@
 import java.util.List;
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 import static io.arlas.server.core.utils.CheckParams.isBboxLatLonInCorrectRanges;
 
@@ -231,6 +232,9 @@ public Response doKVP(
             @ApiParam(hidden = true)
             @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
 
+
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations,
             // --------------------------------------------------------
             // ----------------------- FORM -----------------------
             // --------------------------------------------------------
@@ -337,7 +341,7 @@ public Response doKVP(
                 String serviceUrl = serverBaseUri + "ogc/csw/?";
                 getCapabilitiesHandler.setCapabilitiesType(responseSections, serviceUrl, serverBaseUri + "ogc/csw/opensearch");
                 if (cswHandler.inspireConfiguration.enabled) {
-                    collections = collectionReferenceService.getAllCollectionReferences(columnFilter);
+                    collections = collectionReferenceService.getAllCollectionReferences(columnFilter, organisations);
 
                     collections.removeIf(collectionReference -> collectionReference.collectionName.equals(getMetacollectionName()));
                     filterCollectionsByColumnFilter(columnFilter, collections);
@@ -419,7 +423,7 @@ public Response opensearch(
 
     private CollectionReferences getCollectionReferencesForGetRecords(String[] elements, String[]
             excludes, int maxRecords, int startPosition, String[] ids, String q, String constraint, BoundingBox boundingBox) throws IOException, ArlasException {
-        CollectionReference metacollection = collectionReferenceService.getCollectionReference(getMetacollectionName());
+        CollectionReference metacollection = collectionReferenceService.getCollectionReference(getMetacollectionName(), Optional.empty());
 
         // First we check if there is only "metacollection" that is returned. If this is the case, it means that the queried param is a config param. Thus all collections should be returned
         CollectionReferences collectionReferences = ogcDao.getCollectionReferences(elements, null, 2, startPosition - 1, ids, q, constraint, boundingBox);
diff --git a/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWService.java b/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWService.java
index 03c4276e2..550955550 100644
--- a/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWService.java
+++ b/ogc-csw/src/main/java/io/arlas/server/ogc/csw/CSWService.java
@@ -47,7 +47,7 @@ public CSWService(CollectionReferenceService collectionReferenceService, OGCColl
     }
 
     private void initMetaCollection(String index, OGCConfiguration ogcConfiguration, InspireConfiguration inspireConfiguration) throws ArlasException {
-        List<CollectionReference> collectionReferences =  collectionReferenceService.getAllCollectionReferences(Optional.empty());
+        List<CollectionReference> collectionReferences =  collectionReferenceService.getAllCollectionReferences(Optional.empty(), Optional.empty());
         long count = collectionReferences.stream().filter(collectionReference -> collectionReference.collectionName.equals(getMetacollectionName())).count();
         if (count > 0) {
             collectionReferenceService.deleteCollectionReference(getMetacollectionName());
diff --git a/ogc-wfs/src/main/java/io/arlas/server/ogc/wfs/WFSRESTService.java b/ogc-wfs/src/main/java/io/arlas/server/ogc/wfs/WFSRESTService.java
index ea38a2a07..16bfceee0 100644
--- a/ogc-wfs/src/main/java/io/arlas/server/ogc/wfs/WFSRESTService.java
+++ b/ogc-wfs/src/main/java/io/arlas/server/ogc/wfs/WFSRESTService.java
@@ -57,8 +57,7 @@
 import java.util.Map;
 import java.util.Optional;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 @Path("/ogc/wfs")
 @Api(value = "/ogc/wfs")
@@ -188,7 +187,10 @@ public Response doKVP(
             @ApiParam(hidden = true)
             @HeaderParam(value = PARTITION_FILTER) String partitionFilter,
             @ApiParam(hidden = true)
-            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
+            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
+
 
     ) throws IOException, ArlasException {
 
@@ -201,7 +203,7 @@ public Response doKVP(
         startindex = Optional.ofNullable(startindex).orElse(0);
         count = Optional.ofNullable(count).orElse(ogcConfiguration.queryMaxFeature.intValue());
 
-        CollectionReference collectionReference = collectionReferenceService.getCollectionReference(collection);
+        CollectionReference collectionReference = collectionReferenceService.getCollectionReference(collection, organisations);
         if (collectionReference == null) {
             throw new OGCException(OGCExceptionCode.NOT_FOUND, "Collection not found " + collection, Service.WFS);
         }
diff --git a/stac-api/src/main/java/io/arlas/server/stac/api/StacCollectionsRESTService.java b/stac-api/src/main/java/io/arlas/server/stac/api/StacCollectionsRESTService.java
index ddcac5722..419528d7d 100644
--- a/stac-api/src/main/java/io/arlas/server/stac/api/StacCollectionsRESTService.java
+++ b/stac-api/src/main/java/io/arlas/server/stac/api/StacCollectionsRESTService.java
@@ -50,8 +50,7 @@
 import java.util.List;
 import java.util.Optional;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class StacCollectionsRESTService extends StacRESTService {
 
@@ -87,7 +86,11 @@ For each collection, a link to the items in the collection (path `/collections/{
             @ApiResponse(code = 500, message = "Arlas Server Error.", response = Error.class)})
     public Response getCollections(@Context UriInfo uriInfo,
                                    @ApiParam(hidden = true)
-                                   @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
+                                   @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+                                   @ApiParam(hidden = true)
+                                   @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
+
                                    ) throws ArlasException {
 
         List<StacLink> links = new ArrayList<>(); // TODO what do we put in there?
@@ -95,7 +98,7 @@ public Response getCollections(@Context UriInfo uriInfo,
 
         List<Collection> collectionList = new ArrayList<>();
         for (CollectionReference c :
-                collectionReferenceService.getAllCollectionReferences(columnFilter)
+                collectionReferenceService.getAllCollectionReferences(columnFilter, organisations)
                         .stream()
                         .filter(c -> !c.collectionName.equals("metacollection"))
                         .toList()) {
@@ -131,9 +134,13 @@ The response contains a link to the items in the collection (path `/collections/
             @ApiResponse(code = 500, message = "Arlas Server Error.", response = Error.class)})
     public Response describeCollection(@Context UriInfo uriInfo,
                                        @ApiParam(name = "collectionId", value = "Local identifier of a collection", required = true)
-                                       @PathParam(value = "collectionId") String collectionId) throws ArlasException {
+                                       @PathParam(value = "collectionId") String collectionId,
+
+                                       @ApiParam(hidden = true)
+                                       @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
+    ) throws ArlasException {
 
-        return cache(Response.ok(getCollection(collectionReferenceService.getCollectionReference(collectionId), uriInfo)), 0);
+        return cache(Response.ok(getCollection(collectionReferenceService.getCollectionReference(collectionId, organisations), uriInfo)), 0);
     }
 
     @Timed
@@ -228,10 +235,13 @@ However, in cases where the box spans the antimeridian the first value (west-mos
                                 @HeaderParam(value = PARTITION_FILTER) String partitionFilter,
 
                                 @ApiParam(hidden = true)
-                                @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
+                                @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+                                @ApiParam(hidden = true)
+                                @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
                                 ) throws ArlasException {
 
-        CollectionReference collectionReference = collectionReferenceService.getCollectionReference(collectionId);
+        CollectionReference collectionReference = collectionReferenceService.getCollectionReference(collectionId, organisations);
         String dateFilter = getDateFilter(datetime, collectionReference);
         String geoFilter = getGeoFilter(getBboxAsList(bbox), collectionReference);
 
@@ -273,10 +283,14 @@ public Response getFeature(@Context UriInfo uriInfo,
                                    @HeaderParam(value = PARTITION_FILTER) String partitionFilter,
 
                                @ApiParam(hidden = true)
-                               @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
+                               @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+                               @ApiParam(hidden = true)
+                               @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
+
     ) throws ArlasException {
 
-        CollectionReference collectionReference = collectionReferenceService.getCollectionReference(collectionId);
+        CollectionReference collectionReference = collectionReferenceService.getCollectionReference(collectionId, organisations);
 
         StacFeatureCollection features = getStacFeatureCollection(collectionReference, partitionFilter, columnFilter, null,
                 java.util.Collections.singletonList(getIdFilter(featureId, collectionReference)),
diff --git a/stac-api/src/main/java/io/arlas/server/stac/api/StacCoreRESTService.java b/stac-api/src/main/java/io/arlas/server/stac/api/StacCoreRESTService.java
index e19896fb1..5d2d60222 100644
--- a/stac-api/src/main/java/io/arlas/server/stac/api/StacCoreRESTService.java
+++ b/stac-api/src/main/java/io/arlas/server/stac/api/StacCoreRESTService.java
@@ -29,11 +29,12 @@
 import io.arlas.server.stac.model.LandingPage;
 import io.arlas.server.stac.model.StacLink;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
-import org.apache.http.client.utils.URIBuilder;
 
 import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.client.Client;
@@ -46,6 +47,8 @@
 import java.util.List;
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
+import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 import static javax.ws.rs.core.UriBuilder.fromUri;
 
 public class StacCoreRESTService extends StacRESTService {
@@ -78,7 +81,14 @@ public StacCoreRESTService(STACConfiguration configuration,
                     "`service-desc` and `service-doc`) and the Feature Collection (path `/collections`, link relation " +
                     "`data`).", response = LandingPage.class, responseContainer = "LandingPage"),
             @ApiResponse(code = 500, message = "Arlas Server Error.", response = Error.class)})
-    public Response getLandingPage(@Context UriInfo uriInfo) throws ArlasException {
+    public Response getLandingPage(@Context UriInfo uriInfo,
+                                   @ApiParam(hidden = true)
+                                   @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+                                   @ApiParam(hidden = true)
+                                   @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
+
+    ) throws ArlasException {
 
         List<StacLink> links = new ArrayList<>();
         links.add(getSelfLink(uriInfo));
@@ -89,7 +99,7 @@ public Response getLandingPage(@Context UriInfo uriInfo) throws ArlasException {
         links.add(getLink(uriInfo, "collections", "data", MediaType.APPLICATION_JSON));
         links.add(getLink(uriInfo, "search", "search", "application/geo+json"));
         links.add(getLink(uriInfo, "search", "POST", "search", "application/geo+json"));
-        collectionReferenceService.getAllCollectionReferences(Optional.empty()).forEach(c -> {
+        collectionReferenceService.getAllCollectionReferences(columnFilter, organisations).forEach(c -> {
             if (!c.collectionName.equals("metacollection")) {
                 links.add(getLink(uriInfo, "collections/" + c.collectionName, "child", MediaType.APPLICATION_JSON));
             }
diff --git a/stac-api/src/main/java/io/arlas/server/stac/api/StacSearchRESTService.java b/stac-api/src/main/java/io/arlas/server/stac/api/StacSearchRESTService.java
index 611548145..6747d93ce 100644
--- a/stac-api/src/main/java/io/arlas/server/stac/api/StacSearchRESTService.java
+++ b/stac-api/src/main/java/io/arlas/server/stac/api/StacSearchRESTService.java
@@ -56,8 +56,7 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
-import static io.arlas.commons.rest.utils.ServerConstants.PARTITION_FILTER;
+import static io.arlas.commons.rest.utils.ServerConstants.*;
 
 public class StacSearchRESTService extends StacRESTService {
 
@@ -167,7 +166,10 @@ However, in cases where the box spans the antimeridian the first value (west-mos
             @HeaderParam(value = PARTITION_FILTER) String partitionFilter,
 
             @ApiParam(hidden = true)
-            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
+            @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+            @ApiParam(hidden = true)
+            @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
 
     ) throws ArlasException, JsonProcessingException {
         collections = collections.stream().flatMap(e -> Stream.of(e.split(","))).collect(Collectors.toList());
@@ -186,7 +188,7 @@ However, in cases where the box spans the antimeridian the first value (west-mos
             searchBody.setIntersects(GeoUtil.geojsonReader.readValue(intersects));
         }
 
-        return cache(Response.ok(getItems(partitionFilter, columnFilter, uriInfo, searchBody, "GET")), 0);
+        return cache(Response.ok(getItems(partitionFilter, columnFilter, organisations, uriInfo, searchBody, "GET")), 0);
 
     }
 
@@ -210,18 +212,22 @@ public Response postItemSearch(@Context UriInfo uriInfo,
                                    @HeaderParam(value = PARTITION_FILTER) String partitionFilter,
 
                                    @ApiParam(hidden = true)
-                                   @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter
-                                   ) throws ArlasException {
-        return cache(Response.ok(getItems(partitionFilter, columnFilter, uriInfo, body, "POST")), 0);
+                                   @HeaderParam(value = COLUMN_FILTER) Optional<String> columnFilter,
+
+                                   @ApiParam(hidden = true)
+                                   @HeaderParam(value = ARLAS_ORGANISATION) Optional<String> organisations
+
+    ) throws ArlasException {
+        return cache(Response.ok(getItems(partitionFilter, columnFilter, organisations, uriInfo, body, "POST")), 0);
     }
 
     // -----------
 
-    private StacFeatureCollection getItems(String partitionFilter, Optional<String> columnFilter, UriInfo uriInfo, SearchBody body, String method) throws ArlasException {
+    private StacFeatureCollection getItems(String partitionFilter, Optional<String> columnFilter, Optional<String> organisations, UriInfo uriInfo, SearchBody body, String method) throws ArlasException {
         // TODO search in more than the first collection given as parameter
         CollectionReference collectionReference = body.getCollections() == null || body.getCollections().isEmpty() ?
-                collectionReferenceService.getAllCollectionReferences(columnFilter).get(0) :
-                collectionReferenceService.getCollectionReference(body.getCollections().get(0));
+                collectionReferenceService.getAllCollectionReferences(columnFilter, organisations).get(0) :
+                collectionReferenceService.getCollectionReference(body.getCollections().get(0), organisations);
 
         return getStacFeatureCollection(collectionReference, partitionFilter, columnFilter, body,
                 getFilter(collectionReference, body), uriInfo, method, false);


Test patch:
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java b/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java
index 6adbf0cb4..d018555aa 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/CollectionTool.java
@@ -21,6 +21,7 @@
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.arlas.commons.exceptions.ArlasException;
+import io.arlas.server.core.model.CollectionOrganisations;
 import io.arlas.server.core.model.CollectionReferenceParameters;
 import io.arlas.server.core.model.DublinCoreElementName;
 import io.arlas.server.core.model.Inspire;
@@ -34,6 +35,7 @@
 import java.io.InputStreamReader;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.List;
 
 import static io.restassured.RestAssured.given;
 import static io.restassured.RestAssured.when;
@@ -98,6 +100,9 @@ public  void load(long sleepAfter) throws ArlasException {
         params.dublinCoreElementName.title = DataSetTool.DATASET_DUBLIN_CORE_TITLE;
         params.dublinCoreElementName.description = DataSetTool.DATASET_DUBLIN_CORE_DESCRIPTION;
         params.dublinCoreElementName.language = DataSetTool.DATASET_DUBLIN_CORE_LANGUAGE;
+        params.collectionOrganisations = new CollectionOrganisations();
+        params.collectionOrganisations.owner = DataSetTool.DATASET_ORG_OWNER;
+        params.collectionOrganisations.sharedWith = List.of(DataSetTool.DATASET_ORG_SHARED);
 
 
         // PUT new collection
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java b/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java
index 8efbe6fa7..6d4f1c0e5 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/DataSetTool.java
@@ -47,6 +47,8 @@ public class DataSetTool {
     static Logger LOGGER = LoggerFactory.getLogger(DataSetTool.class);
 
     public final static String DATASET_COLLECTION_DISPLAY_NAME="Test Collection";
+    public final static String DATASET_ORG_OWNER="foo";
+    public final static String DATASET_ORG_SHARED="bar";
     public final static String DATASET_INDEX_NAME="dataset";
     public final static String DATASET_ID_PATH="id";
     public final static String DATASET_ID_DESC = "object identifier";
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java
index 5fc032781..824e4da28 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/collections/CollectionServiceIT.java
@@ -31,6 +31,7 @@
 import java.util.*;
 import java.util.stream.Collectors;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 import static io.restassured.RestAssured.*;
 import static org.hamcrest.Matchers.*;
@@ -100,6 +101,7 @@ public void test02GetAllCollections() throws Exception {
         Map<String, Object> jsonAsMap = getJsonAsMap();
         jsonAsMap.put(CollectionReference.INSPIRE_PATH, getInspireJsonAsMap());
         jsonAsMap.put(CollectionReference.DUBLIN_CORE_PATH, getDublinJsonAsMap());
+        jsonAsMap.put(CollectionReference.ORGANISATIONS, getOrganisationsJsonAsMap(false));
 
         // PUT new collection 1
         given().contentType("application/json").body(jsonAsMap)
@@ -108,6 +110,9 @@ public void test02GetAllCollections() throws Exception {
 
         // GET all collections
         getAllCollections(COLLECTION_NAME, COLLECTION_NAME_ACTOR, "collection1");
+        getAllCollectionsWithOrg(DataSetTool.DATASET_ORG_OWNER, COLLECTION_NAME, COLLECTION_NAME_ACTOR, "collection1");
+        getAllCollectionsWithOrg(DataSetTool.DATASET_ORG_SHARED, COLLECTION_NAME, COLLECTION_NAME_ACTOR, "collection1");
+        getAllCollectionsWithOrg("foobar");
 
         // DELETE collection 1
         when().delete(arlasPath + "collections/collection1")
@@ -121,6 +126,10 @@ private void getAllCollections(String... collectionNames) throws InterruptedExce
         getAllCollections(given(), collectionNames);
     }
 
+    private void getAllCollectionsWithOrg(String org, String... collectionNames) throws InterruptedException {
+        getAllCollections(given().header(ARLAS_ORGANISATION, org), collectionNames);
+    }
+
     private void getAllCollections(RequestSpecification given, String... collectionNames) throws InterruptedException {
         int cpt = 0;
         while (cpt >= 0 && cpt < 5) {
@@ -272,6 +281,7 @@ public void test07CollectionWithDescription() throws Exception {
         jsonAsMap.put(CollectionReference.INSPIRE_PATH, getInspireJsonAsMap());
         jsonAsMap.put(CollectionReference.DUBLIN_CORE_PATH, getDublinJsonAsMap());
         jsonAsMap.put(CollectionReference.DISPLAY_NAMES, getCollectionDescriptionJsonAsMap());
+        jsonAsMap.put(CollectionReference.ORGANISATIONS, getOrganisationsJsonAsMap(false));
 
         // PUT new collection
         given().contentType("application/json").body(jsonAsMap)
@@ -288,6 +298,13 @@ public void test07CollectionWithDescription() throws Exception {
                 .body("params.display_names.fields['"+DataSetTool.DATASET_GEOMETRY_PATH+"']", equalTo(DataSetTool.DATASET_GEOMETRY_DESC))
                 .body("params.display_names.fields['"+DataSetTool.DATASET_TIMESTAMP_PATH+"']", equalTo(DataSetTool.DATASET_TIMESTAMP_DESC));
 
+        given().header(ARLAS_ORGANISATION, DataSetTool.DATASET_ORG_OWNER).when().get(arlasPath + "collections/foo_described")
+                .then().statusCode(200);
+        given().header(ARLAS_ORGANISATION, DataSetTool.DATASET_ORG_SHARED).when().get(arlasPath + "collections/foo_described")
+                .then().statusCode(200);
+        given().header(ARLAS_ORGANISATION, "foobar").when().get(arlasPath + "collections/foo_described")
+                .then().statusCode(403);
+
         // DELETE collection
         when().delete(arlasPath + "collections/foo_described")
                 .then().statusCode(200);
@@ -425,7 +442,13 @@ private Map<String, Object> getInspireInvalidTopicCategoryJsonAsMap() {
         return inspireSubJsonAsMap;
     }
 
-
+    private Map<String, Object> getOrganisationsJsonAsMap(boolean isPublic) {
+        Map<String, Object> orgJsonAsMap = new HashMap<>();
+        orgJsonAsMap.put(CollectionReference.ORGANISATIONS_OWNER, DataSetTool.DATASET_ORG_OWNER);
+        orgJsonAsMap.put(CollectionReference.ORGANISATIONS_PUBLIC, isPublic);
+        orgJsonAsMap.put(CollectionReference.ORGANISATIONS_SHARED, List.of(DataSetTool.DATASET_ORG_SHARED));
+        return orgJsonAsMap;
+    }
 
     @Override
     protected String getUrlPath(String collection) {
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java
index 78248cdd1..d508d2269 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/AbstractDescribeTest.java
@@ -26,6 +26,7 @@
 
 import java.util.Optional;
 
+import static io.arlas.commons.rest.utils.ServerConstants.ARLAS_ORGANISATION;
 import static io.arlas.commons.rest.utils.ServerConstants.COLUMN_FILTER;
 import static io.restassured.RestAssured.given;
 import static org.hamcrest.Matchers.equalTo;
@@ -82,6 +83,9 @@ public void testDescribeFeatureWithSpecificColumFilter() throws Exception {
     @Test
     abstract public void testDescribeFeatureWithCollectionBasedColumFiltering() throws Exception;
 
+    @Test
+    public abstract void testDescribeFeatureWithOrganisationFiltering() throws Exception;
+
     /**
      * Path to expected `describe` result
      * @return
@@ -118,6 +122,14 @@ protected ValidatableResponse get(Optional<String> columnFilter) {
         }
     }
 
+    protected ValidatableResponse get(String org) {
+        return given()
+                .header(ARLAS_ORGANISATION, org)
+                .when()
+                .get(getUrlPath("geodata"))
+                .then();
+    }
+
     /**
      * Cannot compare the whole document because there is a dynamic `params.dublin_core_element_name.date` field in.
      * So we compare each field of group of fields one by one to avoid this dynamic one.
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeCollectionServiceIT.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeCollectionServiceIT.java
index a3c392f9b..9667cc435 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeCollectionServiceIT.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeCollectionServiceIT.java
@@ -46,6 +46,13 @@ public void testDescribeFeatureWithCollectionBasedColumFiltering() throws Except
         handleUnavailableCollection(get(Optional.of("notExisting:fullname,notExisting:params,notExisting:geo_params")));
     }
 
+    @Override
+    public void testDescribeFeatureWithOrganisationFiltering() throws Exception {
+        handleMatchingResponse(get(DataSetTool.DATASET_ORG_OWNER), new JsonPath(this.getClass().getClassLoader().getResourceAsStream(getDescribeResultPath())));
+        handleMatchingResponse(get(DataSetTool.DATASET_ORG_SHARED), new JsonPath(this.getClass().getClassLoader().getResourceAsStream(getDescribeResultPath())));
+        handleUnavailableCollection(get("foobar"));
+    }
+
     @Override
     protected String getDescribeResultPath() {
         return "results/" + (DataSetTool.ALIASED_COLLECTION ? "_describe_base_result_aliased.json" : "_describe_base_result.json");
diff --git a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeServiceIT.java b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeServiceIT.java
index a581784fb..df43076b5 100644
--- a/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeServiceIT.java
+++ b/arlas-tests/src/test/java/io/arlas/server/tests/rest/explore/DescribeServiceIT.java
@@ -77,6 +77,13 @@ public void testDescribeFeatureWithCollectionBasedColumFiltering() throws Except
                 .body("[1].collection_name", Matchers.equalTo(COLLECTION_NAME_ACTOR));
     }
 
+    @Override
+    public void testDescribeFeatureWithOrganisationFiltering() throws Exception {
+        get(DataSetTool.DATASET_ORG_OWNER).statusCode(200).body(".", Matchers.iterableWithSize(2));
+        get(DataSetTool.DATASET_ORG_SHARED).statusCode(200).body(".", Matchers.iterableWithSize(2));
+        get("foobar").statusCode(200).body(".", Matchers.iterableWithSize(0));
+    }
+
     protected ValidatableResponse getWithCollectionFilter(Optional<String> collectionFilter) {
         if (collectionFilter.isPresent()) {
             return given()