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

added captcha requirement #605

Merged
merged 20 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN pip install -r requirements.txt
COPY . .
RUN mkdir db
RUN mkdir -p static
RUN mkdir media
RUN mkdir media -p
RUN chmod +x start_dev.sh

EXPOSE 5678
Expand Down
2 changes: 2 additions & 0 deletions brightIDfaucet/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def str2bool(v):
MEMCACHED_PASSWORD = os.environ.get("MEMCACHEDCLOUD_PASSWORD")
DEPLOYMENT_ENV = os.environ.get("DEPLOYMENT_ENV")

CLOUDFLARE_TURNSITE_SECRET_KEY = os.environ.get("CLOUDFLARE_TURNSITE_SECRET_KEY")

assert DEPLOYMENT_ENV in ["dev", "main"]


Expand Down
1 change: 1 addition & 0 deletions core/constraints/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
IsFollowingTwitterBatch,
IsFollowinTwitterUser,
)
from core.constraints.captcha import HasVerifiedCloudflareCaptcha


def get_constraint(constraint_label: str) -> ConstraintVerification:
Expand Down
3 changes: 2 additions & 1 deletion core/constraints/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ class ConstraintVerification(ABC):
app_name = ConstraintApp.GENERAL.value
__response_text = ""

def __init__(self, user_profile) -> None:
def __init__(self, user_profile, context=None) -> None:
self.user_profile = user_profile
self._param_values = {}
self.context = context

def get_info(self, *args, **kwargs):
pass
Expand Down
42 changes: 42 additions & 0 deletions core/constraints/captcha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@


from django.http import HttpRequest
from core.constraints.abstract import ConstraintApp, ConstraintVerification
from core.thirdpartyapp.cloudflare import CloudflareUtil


import logging


logger = logging.getLogger(__name__)



class HasVerifiedCloudflareCaptcha(ConstraintVerification):
_param_keys = []
app_name = ConstraintApp.GENERAL.value

@staticmethod
def get_client_ip(request: HttpRequest) -> str:
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
else:
ip = request.META['REMOTE_ADDR']
return ip
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved

def is_observed(self, *args, **kwargs) -> bool:
cloudflare = CloudflareUtil()
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved

if self.context is None or self.context.get("request") is None:
return False

request = self.context["request"]

token = request.data.get("cf-turnstile-response") or request.query_params.get("cf-turnstile-response")


if not token:
return False
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved

return cloudflare.is_verified(token, self.get_client_ip(request))
2 changes: 2 additions & 0 deletions core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.translation import gettext_lazy as _
from core.constraints.captcha import HasVerifiedCloudflareCaptcha
from encrypted_model_fields.fields import EncryptedCharField
from rest_framework.exceptions import ValidationError
from solders.keypair import Keypair
Expand Down Expand Up @@ -155,6 +156,7 @@ class Type(models.TextChoices):
GLMStakingVerification,
IsFollowingTwitterBatch,
IsFollowingFarcasterBatch,
HasVerifiedCloudflareCaptcha,
]

name = models.CharField(
Expand Down
18 changes: 18 additions & 0 deletions core/thirdpartyapp/cloudflare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.conf import settings
import requests


class CloudflareUtil:
def __init__(self) -> None:
self.api_url = "https://challenges.cloudflare.com/turnstile/v0"
self.secret_key = settings.CLOUDFLARE_TURNSITE_SECRET_KEY
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved


def is_verified(self, token: str, ip: str) -> bool:
res = requests.post(self.api_url + "/siteverify", data={
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
"secret": self.secret_key,
"response": token,
"remoteip": ip
})
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved

return res.ok and res.json()["success"]
18 changes: 18 additions & 0 deletions prizetap/migrations/0076_alter_constraint_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2024-08-25 09:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('prizetap', '0075_alter_constraint_name'),
]
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved

operations = [
migrations.AlterField(
model_name='constraint',
name='name',
field=models.CharField(choices=[('core.BrightIDMeetVerification', 'BrightIDMeetVerification'), ('core.BrightIDAuraVerification', 'BrightIDAuraVerification'), ('core.HasNFTVerification', 'HasNFTVerification'), ('core.HasTokenVerification', 'HasTokenVerification'), ('core.HasTokenTransferVerification', 'HasTokenTransferVerification'), ('core.AllowListVerification', 'AllowListVerification'), ('core.HasENSVerification', 'HasENSVerification'), ('core.HasLensProfile', 'HasLensProfile'), ('core.IsFollowingLensUser', 'IsFollowingLensUser'), ('core.BeFollowedByLensUser', 'BeFollowedByLensUser'), ('core.DidMirrorOnLensPublication', 'DidMirrorOnLensPublication'), ('core.DidCollectLensPublication', 'DidCollectLensPublication'), ('core.HasMinimumLensPost', 'HasMinimumLensPost'), ('core.HasMinimumLensFollower', 'HasMinimumLensFollower'), ('core.BeFollowedByFarcasterUser', 'BeFollowedByFarcasterUser'), ('core.HasMinimumFarcasterFollower', 'HasMinimumFarcasterFollower'), ('core.DidLikedFarcasterCast', 'DidLikedFarcasterCast'), ('core.DidRecastFarcasterCast', 'DidRecastFarcasterCast'), ('core.IsFollowingFarcasterUser', 'IsFollowingFarcasterUser'), ('core.HasFarcasterProfile', 'HasFarcasterProfile'), ('core.BeAttestedBy', 'BeAttestedBy'), ('core.Attest', 'Attest'), ('core.HasDonatedOnGitcoin', 'HasDonatedOnGitcoin'), ('core.HasMinimumHumanityScore', 'HasMinimumHumanityScore'), ('core.HasGitcoinPassportProfile', 'HasGitcoinPassportProfile'), ('core.IsFollowingFarcasterChannel', 'IsFollowingFarcasterChannel'), ('core.BridgeEthToArb', 'BridgeEthToArb'), ('core.IsFollowinTwitterUser', 'IsFollowinTwitterUser'), ('core.BeFollowedByTwitterUser', 'BeFollowedByTwitterUser'), ('core.DidRetweetTweet', 'DidRetweetTweet'), ('core.DidQuoteTweet', 'DidQuoteTweet'), ('core.HasMuonNode', 'HasMuonNode'), ('core.DelegateArb', 'DelegateArb'), ('core.DelegateOP', 'DelegateOP'), ('core.DidDelegateArbToAddress', 'DidDelegateArbToAddress'), ('core.DidDelegateOPToAddress', 'DidDelegateOPToAddress'), ('core.GLMStakingVerification', 'GLMStakingVerification'), ('core.IsFollowingTwitterBatch', 'IsFollowingTwitterBatch'), ('core.IsFollowingFarcasterBatch', 'IsFollowingFarcasterBatch'), ('core.HasVerifiedCloudflareCaptcha', 'HasVerifiedCloudflareCaptcha'), ('prizetap.HaveUnitapPass', 'HaveUnitapPass'), ('prizetap.NotHaveUnitapPass', 'NotHaveUnitapPass'), ('faucet.OptimismDonationConstraint', 'OptimismDonationConstraint'), ('faucet.OptimismClaimingGasConstraint', 'OptimismClaimingGasConstraint')], max_length=255, unique=True),
),
]
3 changes: 2 additions & 1 deletion prizetap/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def __init__(self, *args, **kwargs):
self.user_profile: UserProfile = kwargs["user_profile"]
self.raffle: Raffle = kwargs["raffle"]
self.raffle_data: dict = kwargs.get("raffle_data", dict())
self.request = kwargs.get("request", None)
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved

def can_enroll_in_raffle(self):
if not self.raffle.is_claimable:
Expand All @@ -29,7 +30,7 @@ def check_user_constraints(self, raise_exception=True):
result = dict()
for c in self.raffle.constraints.all():
constraint: ConstraintVerification = get_constraint(c.name)(
self.user_profile
self.user_profile, { "request": self.request }
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
)
constraint.response = c.response
try:
Expand Down
4 changes: 2 additions & 2 deletions prizetap/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def post(self, request, pk):
)

validator = RaffleEnrollmentValidator(
user_profile=user_profile, raffle=raffle, raffle_data=raffle_data
user_profile=user_profile, raffle=raffle, raffle_data=raffle_data, request=request
)

validator.is_valid(self.request.data)
Expand Down Expand Up @@ -192,7 +192,7 @@ def get(self, request, raffle_pk):
reversed_constraints = raffle.reversed_constraints_list
response_constraints = []
validator = RaffleEnrollmentValidator(
user_profile=user_profile, raffle=raffle, raffle_data=raffle_data
user_profile=user_profile, raffle=raffle, raffle_data=raffle_data, request=request
)

validated_constraints = validator.check_user_constraints(raise_exception=False)
Expand Down
6 changes: 3 additions & 3 deletions start_dev.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
python manage.py collectstatic --noinput
python manage.py migrate
python manage.py runserver 0.0.0.0:5678 &
celery -A brightIDfaucet worker -B
# python manage.py migrate
python manage.py runserver 0.0.0.0:5678
# celery -A brightIDfaucet worker -B
18 changes: 18 additions & 0 deletions tokenTap/migrations/0062_alter_constraint_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2024-08-25 09:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tokenTap', '0061_alter_constraint_name'),
]

operations = [
migrations.AlterField(
model_name='constraint',
name='name',
field=models.CharField(choices=[('core.BrightIDMeetVerification', 'BrightIDMeetVerification'), ('core.BrightIDAuraVerification', 'BrightIDAuraVerification'), ('core.HasNFTVerification', 'HasNFTVerification'), ('core.HasTokenVerification', 'HasTokenVerification'), ('core.HasTokenTransferVerification', 'HasTokenTransferVerification'), ('core.AllowListVerification', 'AllowListVerification'), ('core.HasENSVerification', 'HasENSVerification'), ('core.HasLensProfile', 'HasLensProfile'), ('core.IsFollowingLensUser', 'IsFollowingLensUser'), ('core.BeFollowedByLensUser', 'BeFollowedByLensUser'), ('core.DidMirrorOnLensPublication', 'DidMirrorOnLensPublication'), ('core.DidCollectLensPublication', 'DidCollectLensPublication'), ('core.HasMinimumLensPost', 'HasMinimumLensPost'), ('core.HasMinimumLensFollower', 'HasMinimumLensFollower'), ('core.BeFollowedByFarcasterUser', 'BeFollowedByFarcasterUser'), ('core.HasMinimumFarcasterFollower', 'HasMinimumFarcasterFollower'), ('core.DidLikedFarcasterCast', 'DidLikedFarcasterCast'), ('core.DidRecastFarcasterCast', 'DidRecastFarcasterCast'), ('core.IsFollowingFarcasterUser', 'IsFollowingFarcasterUser'), ('core.HasFarcasterProfile', 'HasFarcasterProfile'), ('core.BeAttestedBy', 'BeAttestedBy'), ('core.Attest', 'Attest'), ('core.HasDonatedOnGitcoin', 'HasDonatedOnGitcoin'), ('core.HasMinimumHumanityScore', 'HasMinimumHumanityScore'), ('core.HasGitcoinPassportProfile', 'HasGitcoinPassportProfile'), ('core.IsFollowingFarcasterChannel', 'IsFollowingFarcasterChannel'), ('core.BridgeEthToArb', 'BridgeEthToArb'), ('core.IsFollowinTwitterUser', 'IsFollowinTwitterUser'), ('core.BeFollowedByTwitterUser', 'BeFollowedByTwitterUser'), ('core.DidRetweetTweet', 'DidRetweetTweet'), ('core.DidQuoteTweet', 'DidQuoteTweet'), ('core.HasMuonNode', 'HasMuonNode'), ('core.DelegateArb', 'DelegateArb'), ('core.DelegateOP', 'DelegateOP'), ('core.DidDelegateArbToAddress', 'DidDelegateArbToAddress'), ('core.DidDelegateOPToAddress', 'DidDelegateOPToAddress'), ('core.GLMStakingVerification', 'GLMStakingVerification'), ('core.IsFollowingTwitterBatch', 'IsFollowingTwitterBatch'), ('core.IsFollowingFarcasterBatch', 'IsFollowingFarcasterBatch'), ('core.HasVerifiedCloudflareCaptcha', 'HasVerifiedCloudflareCaptcha'), ('tokenTap.OncePerMonthVerification', 'OncePerMonthVerification'), ('tokenTap.OnceInALifeTimeVerification', 'OnceInALifeTimeVerification'), ('faucet.OptimismHasClaimedGasConstraint', 'OptimismHasClaimedGasConstraint')], max_length=255, unique=True),
),
]
5 changes: 3 additions & 2 deletions tokenTap/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ def is_valid(self, data):

class TokenDistributionValidator:
def __init__(
self, td: TokenDistribution, user_profile: UserProfile, td_data: dict
self, td: TokenDistribution, user_profile: UserProfile, td_data: dict, *args, **kwargs
alimaktabi marked this conversation as resolved.
Show resolved Hide resolved
) -> None:
self.td = td
self.td_data = td_data
self.user_profile = user_profile
self.request = kwargs.get("request")

def check_user_permissions(self, raise_exception=True):
try:
Expand All @@ -54,7 +55,7 @@ def check_user_permissions(self, raise_exception=True):
result = dict()
for c in self.td.constraints.all():
constraint: ConstraintVerification = get_constraint(c.name)(
self.user_profile
self.user_profile, { "request": self.request }
)
constraint.response = c.response
try:
Expand Down
4 changes: 2 additions & 2 deletions tokenTap/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def post(self, request, *args, **kwargs):
pass

validator = TokenDistributionValidator(
token_distribution, user_profile, td_data
token_distribution, user_profile, td_data, request=request
)
validator.is_valid()

Expand Down Expand Up @@ -184,7 +184,7 @@ def get(self, request, td_id):
reversed_constraints = td.reversed_constraints_list
response_constraints = []

validator = TokenDistributionValidator(td, user_profile, td_data)
validator = TokenDistributionValidator(td, user_profile, td_data, request=request)
validated_constraints = validator.check_user_permissions(raise_exception=False)
for c_pk, data in validated_constraints.items():
response_constraints.append(
Expand Down
Loading