Skip to content

Commit

Permalink
Merge pull request #498 from awf-dbca/sec-review
Browse files Browse the repository at this point in the history
Security Review - unauthenticated, external, read-only (server-side)
  • Loading branch information
xzzy authored Jul 30, 2024
2 parents 4802b54 + d68549c commit dfa21a8
Show file tree
Hide file tree
Showing 24 changed files with 304 additions and 204 deletions.
110 changes: 81 additions & 29 deletions boranga/components/conservation_status/api.py

Large diffs are not rendered by default.

56 changes: 43 additions & 13 deletions boranga/components/conservation_status/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,27 @@ def has_permission(self, request, view):

if request.user.is_superuser:
return True

if hasattr(view, "action") and view.action == "create":
return is_contributor(request)


return (
is_readonly_user(request)
or is_conservation_status_assessor(request)
or is_conservation_status_approver(request)
or is_species_communities_approver(request)
or is_occurrence_assessor(request)
or is_occurrence_approver(request)
or is_contributor(request)
or is_conservation_status_referee(request)
)

def has_object_permission(self, request, view, obj):

if request.method in permissions.SAFE_METHODS:
return True

if obj.submitter == request.user.id:
return is_contributor(request)

return is_conservation_status_assessor(
request
) or is_conservation_status_approver(request)

) or is_conservation_status_approver(
request
) or request.user.is_superuser

class ExternalConservationStatusPermission(BasePermission):
def has_permission(self, request, view):
Expand All @@ -101,15 +96,24 @@ def has_permission(self, request, view):

if request.user.is_superuser:
return True

if hasattr(view, "action") and view.action == "create":
return is_contributor(request)

return is_external_contributor(request)
return is_contributor(request)

def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True

if (hasattr(view, "action") and view.action == "reinstate"
and obj.submitter == request.user.id and
obj.processing_status == ConservationStatus.PROCESSING_STATUS_DISCARDED):
return is_contributor(request)

if obj.submitter == request.user.id:
return is_external_contributor(request)
if (obj.submitter == request.user.id and
obj.processing_status == ConservationStatus.PROCESSING_STATUS_DRAFT):
return is_contributor(request)


class ConservationStatusReferralPermission(BasePermission):
Expand Down Expand Up @@ -238,3 +242,29 @@ def has_object_permission(self, request, view, obj):
)

return is_conservation_status_assessor(request)

class ConservationStatusExternalRefereeInvitePermission(BasePermission):
def has_permission(self, request, view):
if not request.user.is_authenticated:
return False

if request.user.is_superuser:
return True

if hasattr(view, "action") and view.action == "create":
return is_conservation_status_assessor(request)

return (
is_readonly_user(request)
or is_conservation_status_assessor(request)
or is_conservation_status_approver(request)
or is_species_communities_approver(request)
or is_occurrence_assessor(request)
or is_occurrence_approver(request)
)

def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True

return is_conservation_status_assessor(request)
10 changes: 4 additions & 6 deletions boranga/components/conservation_status/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
is_conservation_status_approver,
is_conservation_status_assessor,
is_contributor,
is_internal_contributor,
is_new_external_contributor,
is_internal,
)
from boranga.ledger_api_utils import retrieve_email_user

Expand Down Expand Up @@ -314,7 +314,7 @@ def get_internal_user_edit(self, obj):
return (
obj.can_user_edit
and obj.internal_application
and is_internal_contributor(request)
and is_internal(request)
and obj.submitter == request.user.id
)

Expand Down Expand Up @@ -506,7 +506,7 @@ def get_internal_user_edit(self, obj):
return (
obj.can_user_edit
and obj.internal_application
and is_internal_contributor(request)
and is_internal(request)
and obj.submitter == request.user.id
)

Expand All @@ -521,7 +521,6 @@ class BaseConservationStatusSerializer(serializers.ModelSerializer):
readonly = serializers.SerializerMethodField(read_only=True)
group_type = serializers.SerializerMethodField(read_only=True)
group_type_id = serializers.SerializerMethodField(read_only=True)
allowed_assessors = EmailUserSerializer(many=True)

class Meta:
model = ConservationStatus
Expand Down Expand Up @@ -563,7 +562,6 @@ class Meta:
"applicant_details",
"assigned_officer",
"assigned_approver",
"allowed_assessors",
"deficiency_data",
"assessor_data",
"approval_level",
Expand Down Expand Up @@ -841,7 +839,7 @@ def get_internal_user_edit(self, obj):
return (
obj.can_user_edit
and obj.internal_application
and is_internal_contributor(request)
and is_internal(request)
and obj.submitter == request.user.id
)

Expand Down
66 changes: 20 additions & 46 deletions boranga/components/history/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@
from reversion.models import Version

from boranga.helpers import (
is_conservation_status_approver,
is_conservation_status_assessor,
is_django_admin,
is_internal,
is_occurrence_approver,
is_occurrence_assessor,
is_species_communities_approver,
)

logger = logging.getLogger("log")
Expand All @@ -34,79 +28,59 @@ class InternalAuthorizationView(views.APIView):
return data.
"""

django_admin_models = [
"Species",
"SpeciesDocument",
"Community",
"CommunityDocument",
"ConservationStatus",
"ConservationStatusDocument",
"Occurrence",
"OccurrenceReport",
"OccurrenceDocument",
"OccurrenceReportDocument",
"OCRConservationThreat",
"OCCConservationThreat",
"Minutes",
"ConservationThreat",
]
species_communities_approver_models = [
species_communities_models = [
"Community",
"CommunityDocument",
"Species",
"SpeciesDocument",
"ConservationThreat",
]
meeting_models = [
"Minutes",
]
conservation_status_editor_models = [
conservation_status_models = [
"ConservationStatus",
"ConservationStatusDocument",
"Minutes",
]
occurrence_assessor_models = [
"Occurrence",
"OccurrenceReport",
"OccurrenceDocument",
"OccurrenceReportDocument",
"OCRConservationThreat",
"OCCConservationThreat",
]
occurrence_approver_models = [
occurrence_models = [
"Occurrence",
"OccurrenceReport",
"OccurrenceDocument",
"OccurrenceReportDocument",
"OCRConservationThreat",
"OCCConservationThreat",
"OCRObserverDetail",
"OCCContactDetail",
"OccurrenceTenure",
"OccurrenceSite",
]

def check_auth_by_model(self, request, model_name):
if request.user.is_superuser:
return True
else:
# go through each list, if model is in it run function for user
# go through each list, if model is in it run function for allowed group(s)
# return the result if true, otherwise run other checks until all possibilities exhausted
if model_name in self.django_admin_models and is_django_admin(request):
return True
#NOTE: currently redundant as is_internal is not constrained to internal group members and all
# internal group members should have access - but might be worth keeping as is in case granularity required
if (
model_name in self.species_communities_approver_models
and is_species_communities_approver(request)
model_name in self.species_communities_models
and is_internal(request)
):
return True
if (
model_name in self.conservation_status_editor_models
and is_conservation_status_assessor(request)
or is_conservation_status_approver(request)
model_name in self.conservation_status_models
and is_internal(request)
):
return True
if (
model_name in self.occurrence_assessor_models
and is_occurrence_assessor(request)
model_name in self.meeting_models
and is_internal(request)
):
return True
if (
model_name in self.occurrence_approver_models
and is_occurrence_approver(request)
model_name in self.occurrence_models
and is_internal(request)
):
return True

Expand Down
Loading

0 comments on commit dfa21a8

Please sign in to comment.