From 1e907a42fea491bc698ae854dbceb93aef705ba5 Mon Sep 17 00:00:00 2001 From: Shayan Shiravani Date: Sat, 25 Nov 2023 15:41:35 +0330 Subject: [PATCH] Prizetap reversed constraints --- .../0044_raffle_reversed_constraints.py | 18 +++++++ prizetap/models.py | 1 + prizetap/serializers.py | 8 +++ prizetap/tests.py | 51 ++++++++++++++++++- prizetap/validators.py | 9 +++- 5 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 prizetap/migrations/0044_raffle_reversed_constraints.py diff --git a/prizetap/migrations/0044_raffle_reversed_constraints.py b/prizetap/migrations/0044_raffle_reversed_constraints.py new file mode 100644 index 00000000..393c83d2 --- /dev/null +++ b/prizetap/migrations/0044_raffle_reversed_constraints.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.4 on 2023-11-23 09:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('prizetap', '0043_constraint_explanation'), + ] + + operations = [ + migrations.AddField( + model_name='raffle', + name='reversed_constraints', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/prizetap/models.py b/prizetap/models.py index 585be4e6..29ac6928 100644 --- a/prizetap/models.py +++ b/prizetap/models.py @@ -64,6 +64,7 @@ class Meta: constraints = models.ManyToManyField(Constraint, blank=True, related_name="raffles") constraint_params = models.TextField(null=True, blank=True) + reversed_constraints = models.TextField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True, editable=True) start_at = models.DateTimeField(default=timezone.now) diff --git a/prizetap/serializers.py b/prizetap/serializers.py index dc551eac..1cf26212 100644 --- a/prizetap/serializers.py +++ b/prizetap/serializers.py @@ -113,6 +113,9 @@ def validate(self, data): constraints = data["constraints"] constraint_params = json.loads(base64.b64decode(data["constraint_params"])) data["constraint_params"] = base64.b64decode(data["constraint_params"]).decode("utf-8") + reversed_constraints = [] + if "reversed_constraints" in data: + reversed_constraints = str(data["reversed_constraints"]).split(",") if len(constraints) != 0: for c in constraints: constraint_class: ConstraintVerification = get_constraint(c.name) @@ -121,6 +124,11 @@ def validate(self, data): constraint_class.is_valid_param_keys(constraint_params[c.name]) except KeyError as e: raise serializers.ValidationError({"constraint_params": [{f"{c.name}": str(e)}]}) + valid_constraints = [c.name for c in constraints] + if len(reversed_constraints) > 0: + for c in reversed_constraints: + if c not in valid_constraints: + raise serializers.ValidationError({"reversed_constraints": [{f"{c}": "Invalid constraint name"}]}) if "winners_count" in data and data["winners_count"] > data["max_number_of_entries"]: raise serializers.ValidationError({"winners_count": "Invalid value"}) valid_chains = list(CONTRACT_ADDRESSES.keys()) diff --git a/prizetap/tests.py b/prizetap/tests.py index 43f6aa18..291310c5 100644 --- a/prizetap/tests.py +++ b/prizetap/tests.py @@ -1,16 +1,19 @@ import base64 import json -from unittest.mock import PropertyMock, patch +from unittest.mock import MagicMock, PropertyMock, patch from django.contrib.auth.models import User from django.urls import reverse from django.utils import timezone +from rest_framework.exceptions import PermissionDenied from rest_framework.test import APITestCase +from authentication.helpers import BrightIDSoulboundAPIInterface from authentication.models import NetworkTypes, UserProfile, Wallet from faucet.models import Chain, WalletAccount from .models import Constraint, NotHaveUnitapPass, Raffle, RaffleEntry +from .validators import RaffleEnrollmentValidator # from .utils import PrizetapContractClient @@ -235,6 +238,52 @@ def test_create_raffle_with_invalid_constraint_params(self): self.assertEqual(raffle, None) + @patch( + "authentication.helpers.BrightIDSoulboundAPIInterface.get_verification_status", + lambda a, b, c: (True, None), + ) + def test_reversed_constraints(self): + self.raffle.reversed_constraints = "core.BrightIDMeetVerification" + self.raffle.save() + validator = RaffleEnrollmentValidator(user_profile=self.user_profile, raffle=self.raffle) + self.assertRaises(PermissionDenied, validator.check_user_constraints) + + def test_create_raffle_with_invalid_reversed_constraints(self): + self.client.force_authenticate(user=self.user_profile.user) + self.raffle_data["constraints"] = [self.meet_constraint.pk] + self.raffle_data["reversed_constraints"] = "core.BrightIDAuraVerification" + response = self.client.post(reverse("create-raffle"), self.raffle_data) + self.assertEqual( + str(response.data["reversed_constraints"][0]["core.BrightIDAuraVerification"]), "Invalid constraint name" + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(Raffle.objects.count(), 1) + raffle = None + try: + raffle = Raffle.objects.get(pk=2) + except Raffle.DoesNotExist: + pass + + self.assertEqual(raffle, None) + + @patch( + "authentication.helpers.BrightIDSoulboundAPIInterface.get_verification_status", + lambda a, b, c: (False, None), + ) + def test_create_raffle_with_reversed_constraints(self): + self.client.force_authenticate(user=self.user_profile.user) + self.raffle_data["constraints"] = [self.meet_constraint.pk] + self.raffle_data["reversed_constraints"] = self.meet_constraint.name + response = self.client.post(reverse("create-raffle"), self.raffle_data) + self.assertEqual(response.status_code, 200) + self.assertEqual(Raffle.objects.count(), 2) + raffle = Raffle.objects.get(pk=response.data["data"]["id"]) + self.assertEqual(raffle.name, self.raffle_data["name"]) + validator = RaffleEnrollmentValidator(user_profile=self.user_profile, raffle=raffle) + validator.check_user_constraints() + BrightIDSoulboundAPIInterface.get_verification_status = MagicMock(return_value=(True, None)) + self.assertRaises(PermissionDenied, validator.check_user_constraints) + def test_create_raffle_with_invalid_winners_count(self): self.client.force_authenticate(user=self.user_profile.user) self.raffle_data["max_number_of_entries"] = 10 diff --git a/prizetap/validators.py b/prizetap/validators.py index 1a1f466e..7c0c8c5a 100644 --- a/prizetap/validators.py +++ b/prizetap/validators.py @@ -22,6 +22,7 @@ def check_user_constraints(self): param_values = json.loads(self.raffle.constraint_params) except Exception: param_values = {} + reversed_constraints = self.raffle.reversed_constraints.split(",") if self.raffle.reversed_constraints else [] for c in self.raffle.constraints.all(): constraint: ConstraintVerification = get_constraint(c.name)(self.user_profile) constraint.response = c.response @@ -29,8 +30,12 @@ def check_user_constraints(self): constraint.param_values = param_values[c.name] except KeyError: pass - if not constraint.is_observed(): - raise PermissionDenied(constraint.response) + if c.name in reversed_constraints: + if constraint.is_observed(): + raise PermissionDenied(constraint.response) + else: + if not constraint.is_observed(): + raise PermissionDenied(constraint.response) def check_user_has_wallet(self): if not self.user_profile.wallets.filter(wallet_type=self.raffle.chain.chain_type).exists():