Skip to content

Commit

Permalink
Merge pull request #11455 from archesproject/adg/11336-deny-mvt
Browse files Browse the repository at this point in the history
initial fix for default deny for mvt tiles
  • Loading branch information
chiatt committed Sep 20, 2024
2 parents 51965c9 + 52dd9a9 commit 6df07fb
Show file tree
Hide file tree
Showing 8 changed files with 646 additions and 265 deletions.
21 changes: 14 additions & 7 deletions arches/app/models/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@
)
from arches.app.utils.permission_backend import (
user_is_resource_reviewer,
get_restricted_instances,
user_can_read_graph,
get_filtered_instances,
get_nodegroups_by_perm,
)
import django.dispatch
Expand Down Expand Up @@ -867,9 +866,6 @@ def get_relations(
ret["total"] = {"value": resource_relations["total"]}
instanceids = set()

restricted_instances = (
get_restricted_instances(user, se) if user is not None else []
)
readable_graphids = set(
permission_backend.get_resource_types_by_perm(
user, ["models.read_nodegroup"]
Expand All @@ -881,10 +877,21 @@ def get_relations(
resourceid_from = relation["resourceinstanceidfrom"]
resourceinstanceto_graphid = relation["resourceinstanceto_graphid"]
resourceinstancefrom_graphid = relation["resourceinstancefrom_graphid"]
exclusive_set, filtered_instances = get_filtered_instances(
user, se, resources=[resourceid_from, resourceid_to]
)
filtered_instances = filtered_instances if user is not None else []

resourceid_to_permission = resourceid_to not in filtered_instances
resourceid_from_permission = resourceid_from not in filtered_instances

if exclusive_set:
resourceid_to_permission = not (resourceid_to_permission)
resourceid_from_permission = not (resourceid_from_permission)

if (
resourceid_to not in restricted_instances
and resourceid_from not in restricted_instances
resourceid_to_permission
and resourceid_from_permission
and str(resourceinstanceto_graphid) in readable_graphids
and str(resourceinstancefrom_graphid) in readable_graphids
):
Expand Down
83 changes: 81 additions & 2 deletions arches/app/permissions/arches_default_allow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from guardian.models import GroupObjectPermission
from guardian.models import GroupObjectPermission, UserObjectPermission

from arches.app.models.models import ResourceInstance
from arches.app.models.resource import Resource
Expand All @@ -14,13 +14,17 @@
ArchesPermissionBase,
ResourceInstancePermissions,
)
from arches.app.search.elasticsearch_dsl_builder import Bool, Terms, Nested
from arches.app.search.elasticsearch_dsl_builder import Bool, Ids, Terms, Nested, Query
from arches.app.search.mappings import RESOURCES_INDEX
from arches.app.search.search import SearchEngine


logger = logging.getLogger(__name__)


class ArchesDefaultAllowPermissionFramework(ArchesPermissionBase):
is_exclusive = False

def process_new_user(self, instance: User, created: bool) -> None:
ct = ContentType.objects.get(app_label="models", model="resourceinstance")
resourceInstanceIds = list(
Expand Down Expand Up @@ -162,6 +166,81 @@ def get_restricted_users(self, resource: ResourceInstance) -> dict[str, set[int]

return result

def get_filtered_instances(
self,
user: User,
search_engine: SearchEngine | None = None,
allresources: bool = False,
resources: list[str] | None = None,
):
allowed_instances = self.get_restricted_instances(
user, search_engine, allresources, resources
)

return (self.__class__.is_exclusive, allowed_instances)

def get_restricted_instances(
self,
user: User,
search_engine: SearchEngine | None = None,
allresources: bool = False,
resources: list[str] = None,
) -> list[str]:
if allresources is False and user.is_superuser is True:
return []

if allresources is True:
group_object_permissions = GroupObjectPermission.objects.filter(
permission__codename="no_access_to_resourceinstance"
)
if resources is not None:
group_object_permissions.filter(object_pk__in=resources)

restricted_group_instances = {
perm["object_pk"]
for perm in group_object_permissions.values("object_pk")
}
user_object_permissions = UserObjectPermission.objects.filter(
permission__codename="no_access_to_resourceinstance"
)
if resources is not None:
user_object_permissions.filter(object_pk__in=resources)

restricted_user_instances = {
perm["object_pk"]
for perm in user_object_permissions.values("object_pk")
}
all_restricted_instances = list(
restricted_group_instances | restricted_user_instances
)
return all_restricted_instances
else:
terms = Terms(field="permissions.users_with_no_access", terms=[str(user.id)]) # type: ignore
query = Query(search_engine, start=0, limit=settings.SEARCH_RESULT_LIMIT) # type: ignore
has_access = Bool() # type: ignore
nested_term_filter = Nested(path="permissions", query=terms) # type: ignore
has_access.must(nested_term_filter) # type: ignore
if resources is not None:
has_access.filter(
Ids(
ids=resources,
)
)

query.add_query(has_access) # type: ignore
results = query.search(index=RESOURCES_INDEX, scroll="1m") # type: ignore
scroll_id = results["_scroll_id"]
total = results["hits"]["total"]["value"]
if total > settings.SEARCH_RESULT_LIMIT:
pages = total // settings.SEARCH_RESULT_LIMIT
for page in range(pages):
results_scrolled = query.se.es.scroll(
scroll_id=scroll_id, scroll="1m"
)
results["hits"]["hits"] += results_scrolled["hits"]["hits"]
restricted_ids = [res["_id"] for res in results["hits"]["hits"]]
return restricted_ids

def check_resource_instance_permissions(
self, user: User, resourceid: str, permission: str
) -> ResourceInstancePermissions:
Expand Down
86 changes: 83 additions & 3 deletions arches/app/permissions/arches_default_deny.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,108 @@
ArchesPermissionBase,
ResourceInstancePermissions,
)
from arches.app.search.elasticsearch_dsl_builder import Bool, Nested, Terms
from arches.app.search.elasticsearch_dsl_builder import Bool, Query, Terms, Nested, Ids
from arches.app.search.search import SearchEngine
from arches.app.search.mappings import RESOURCES_INDEX


class ArchesDefaultDenyPermissionFramework(ArchesPermissionBase):
is_exclusive = True

def get_sets_for_user(self, user: User, perm: str) -> set[str] | None:
# We do not do set filtering - None is allow-all for sets.
return None if user and user.username != "anonymous" else set()

def get_restricted_users(self, resource: ResourceInstance) -> dict[str, list[int]]:
pass

def get_filtered_instances(
self,
user: User,
search_engine: SearchEngine | None = None,
allresources: bool = False,
resources: list[str] | None = None,
):
allowed_instances = self.get_allowed_instances(
user, search_engine, allresources, resources
)

return (self.__class__.is_exclusive, allowed_instances)

def get_allowed_instances(
self,
user: User,
search_engine: SearchEngine | None = None,
allresources: bool = False,
resources: list[str] | None = None,
):
all = False
if user.is_superuser is True:
if resources is not None:
return resources
else:
all = True

query = Query(search_engine, start=0, limit=settings.SEARCH_RESULT_LIMIT) # type: ignore
nested_groups_read = Nested(
path="permissions",
query=Terms(
field="permissions.groups_read",
terms=[str(group.id) for group in user.groups.all()],
),
)

nested_users_read = Nested(
path="permissions",
query=Terms(field="permissions.users_read", terms=[str(user.id)]),
)

if not all:
if resources is not None:
subset_query = Bool()
subset_query = (
subset_query.filter(
Ids(
ids=resources,
)
)
.should(nested_users_read)
.should(nested_groups_read)
)
query.add_query(subset_query)
else:
query.add_query(Bool().should(nested_groups_read).should(nested_users_read)) # type: ignore

results = query.search(index=RESOURCES_INDEX, scroll="1m") # type: ignore
scroll_id = results["_scroll_id"]
total = results["hits"]["total"]["value"]
if total > settings.SEARCH_RESULT_LIMIT:
pages = total // settings.SEARCH_RESULT_LIMIT
for page in range(pages):
results_scrolled = query.se.es.scroll(scroll_id=scroll_id, scroll="1m")
results["hits"]["hits"] += results_scrolled["hits"]["hits"]
restricted_ids = [res["_id"] for res in results["hits"]["hits"]]
return restricted_ids

def check_resource_instance_permissions(
self, user: User, resourceid: str, permission: str
) -> ResourceInstancePermissions:

result = ResourceInstancePermissions()
resource = ResourceInstance.objects.get(resourceinstanceid=resourceid)
if resourceid == settings.SYSTEM_SETTINGS_RESOURCE_ID:
result["resource"] = resource
if not user.groups.filter(name="System Administrator").exists():
result["permitted"] = False
return result
else:
result["permitted"] = True
return result

if resource.principaluser_id == user.id:
result["permitted"] = True
result["resource"] = resource
return result

resource = ResourceInstance.objects.get(resourceinstanceid=resourceid)
result["resource"] = resource
result["permitted"] = False # by default, deny

Expand Down
Loading

0 comments on commit 6df07fb

Please sign in to comment.