Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prizetap reversed constraints #197

Merged
merged 1 commit into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions prizetap/migrations/0044_raffle_reversed_constraints.py
Original file line number Diff line number Diff line change
@@ -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),
),
]
1 change: 1 addition & 0 deletions prizetap/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions prizetap/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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())
Expand Down
51 changes: 50 additions & 1 deletion prizetap/tests.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions prizetap/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ 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
try:
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():
Expand Down