Skip to content

Commit

Permalink
New permission to restrict modification of model runs
Browse files Browse the repository at this point in the history
New permission prevents creation or modification (deletion, renaming) of existing model runs based on a model_area level setting. Not applied by default to any model areas, but we have a READ_ONLY feature package included in this commit that sets that flag and we can load it to a new model area
  • Loading branch information
nickrsan committed Nov 17, 2023
1 parent d9cf8ea commit 83c85df
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 2 deletions.
4 changes: 4 additions & 0 deletions waterspout_api/feature_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
'allow_static_regions': False,
'allow_removed_regions': False,
'allow_linear_scaled_regions': False,
'create_model_runs': True,
'shared_model_runs': True, # should model runs be visible to all organization members?
}

Expand All @@ -40,6 +41,9 @@
DEBUG['include_net_revenue'] = True
DEBUG['allow_model_run_creation_code_view'] = True

READ_ONLY = copy.deepcopy(FULL_PUBLIC) # READ_ONLY means they can't create new models, but they can use all the other tools
READ_ONLY['create_or_modify_model_runs'] = False

DAP_DSC = copy.deepcopy(FULL_PUBLIC)

# a few special plans - we'll copy them for now in case they have modifications later
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.1.9 on 2023-11-16 23:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('waterspout_api', '0055_load_dap_multipliers'),
]

operations = [
migrations.AddField(
model_name='modelareapreferences',
name='create_or_modify_model_runs',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='regiongroup',
name='regions',
field=models.ManyToManyField(related_name='groups', to='waterspout_api.region'),
),
]
5 changes: 5 additions & 0 deletions waterspout_api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ class ModelAreaPreferences(models.Model):
# should all users of this model area be able to see the model runs of all other users?
shared_model_runs = models.BooleanField(default=True)

# should users of this model area be able to create new model runs in the model area or just view model
# runs we've already loaded? Useful to disable when we want to premake some model runs then release
# the results without releasing the tools to create model runs
create_or_modify_model_runs = models.BooleanField(default=True)

# prevent users from reducing price/yield below the value that would make profits negative
# basically forces stormchaser to create cards for crops when All Crops
# goes to negative profits for the crop
Expand Down
31 changes: 31 additions & 0 deletions waterspout_api/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,34 @@ def _check_org_info(self, request_user, request_data, view):
# request_data["calibration_set"] = calibration_set # replace it with the object so we can assign it later

return True


class CanCreateOrModifyModelRuns(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS: # allow them to read model runs with this permission
return True
else: # but if they want to create, we need to check if the ModelArea allows creation
if "pk" in view.kwargs: # then we're checking against an existing object
item_id = view.kwargs['pk']
item_class = view.serializer_class.Meta.model
try:
item = item_class.objects.get(pk=item_id)
except item_class.DoesNotExist:
return PermissionError("Model Run doesn't exist")

if hasattr(item, "model_area"):
model_area = item.model_area
elif hasattr(item, "calibration_set"):
model_area = item.calibration_set.model_area
else:
raise RuntimeError(f"Can't get model area from {view.serializer_class}")
else:
if type(request.data) is not dict:
request_data = json.loads(request.data)
else:
request_data = request.data
calibration_set = models.CalibrationSet.objects.get(pk=request_data["calibration_set"])
model_area = calibration_set.model_area

preferences = model_area.preferences
return preferences.create_or_modify_model_runs
4 changes: 2 additions & 2 deletions waterspout_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,9 @@ class ModelRunViewSet(viewsets.ModelViewSet):
Test
Permissions: Must be in same organization to specifically request an item
Permissions: Must be in same organization to specifically request an item, and ModelArea must allow model run creation to send destructive actions to the model run
"""
permission_classes = [permissions.IsInSameOrganization]
permission_classes = [permissions.IsInSameOrganization, permissions.CanCreateOrModifyModelRuns]
serializer_class = serializers.ModelRunSerializer

def get_queryset(self):
Expand Down

0 comments on commit 83c85df

Please sign in to comment.