Skip to content

Commit

Permalink
add 'revoked' throttle scope to enable easily revoking openverse TOS …
Browse files Browse the repository at this point in the history
…violating client application access.
  • Loading branch information
madewithkode committed May 15, 2024
1 parent 6c1ae8d commit ad8bdc1
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-05-15 09:43

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0061_convert_varchar_to_text'),
]

operations = [
migrations.AlterField(
model_name='throttledapplication',
name='rate_limit_model',
field=models.CharField(choices=[('standard', 'standard'), ('enhanced', 'enhanced'), ('exempt', 'exempt'), ('revoked', 'revoked')], default='standard', max_length=20),
),
]
1 change: 1 addition & 0 deletions api/api/models/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ThrottledApplication(AbstractApplication):
# case-by-case basis.
("exempt", "exempt"), # Rate limits used for internal infrastructure to
# by-pass rate limiting entirely.
("revoked", "revoked"), # Rate limits used for outrightly disallowing access.
]
rate_limit_model = models.CharField(
max_length=20, choices=RATE_LIMIT_MODELS, default="standard"
Expand Down
5 changes: 5 additions & 0 deletions api/api/utils/throttle.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,8 @@ class EnhancedOAuth2IdBurstRateThrottle(AbstractOAuth2IdRateThrottle):
class ExemptOAuth2IdRateThrottle(AbstractOAuth2IdRateThrottle):
applies_to_rate_limit_model = {"exempt"}
scope = "exempt_oauth2_client_credentials"


class RevokedOAuth2IdRateThrottle(AbstractOAuth2IdRateThrottle):
applies_to_rate_limit_model = {"revoked"}
scope = "revoked"
14 changes: 12 additions & 2 deletions api/conf/oauth2_extensions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.exceptions import (
AuthenticationFailed,
PermissionDenied,
)

from drf_spectacular.authentication import TokenScheme
from oauth2_provider.contrib.rest_framework import (
Expand All @@ -18,7 +21,14 @@ def authenticate(self, request):
# requests with valid credentials
# `request` is mutated by `super().authenticate`
raise AuthenticationFailed()

# if this is an authed request, check and
# deny access if client's access has been
# revoked.
if getattr(request, "auth", None):
auth_user = getattr(request, "auth")
application = getattr(auth_user, "application", None)
if application and application.rate_limit_model == "revoked":
raise PermissionDenied()
return result


Expand Down
3 changes: 3 additions & 0 deletions api/conf/settings/rest_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
"enhanced_oauth2_client_credentials_burst": "200/min",
# ``None`` completely by-passes the rate limiting
"exempt_oauth2_client_credentials": None,
# Outrightly deny access
"revoked": "0/day",
}

DEFAULT_THROTTLE_CLASSES = (
Expand All @@ -50,6 +52,7 @@
"api.utils.throttle.EnhancedOAuth2IdBurstRateThrottle",
"api.utils.throttle.EnhancedOAuth2IdSustainedRateThrottle",
"api.utils.throttle.ExemptOAuth2IdRateThrottle",
"api.utils.throttle.RevokedOAuth2IdRateThrottle",
)

REST_FRAMEWORK = {
Expand Down
2 changes: 1 addition & 1 deletion api/latest_migrations/api
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# If you have a merge conflict in this file, it means you need to run:
# manage.py makemigrations --merge
# in order to resolve the conflict between migrations.
0061_convert_varchar_to_text
0062_alter_throttledapplication_rate_limit_model
5 changes: 3 additions & 2 deletions api/test/integration/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def _integration_verify_most_recent_token(api_client):
@pytest.mark.django_db
@pytest.mark.parametrize(
"rate_limit_model",
[x[0] for x in ThrottledApplication.RATE_LIMIT_MODELS],
[x[0] for x in ThrottledApplication.RATE_LIMIT_MODELS if not x[0] == "revoked"],
)
@cache_availability_params
@pytest.mark.skipif(
Expand Down Expand Up @@ -129,7 +129,7 @@ def test_auth_email_verification(
@pytest.mark.django_db
@pytest.mark.parametrize(
"rate_limit_model",
[x[0] for x in ThrottledApplication.RATE_LIMIT_MODELS],
[x[0] for x in ThrottledApplication.RATE_LIMIT_MODELS if not x[0] == "revoked"],
)
@cache_availability_params
def test_auth_rate_limit_reporting(
Expand Down Expand Up @@ -267,6 +267,7 @@ def test_page_size_limit_authed(api_client, test_auth_token_exchange):
res = api_client.get(
"/v1/images/", query_params, HTTP_AUTHORIZATION=f"Bearer {token}"
)

assert res.status_code == 200

query_params = {"page_size": 500}
Expand Down

0 comments on commit ad8bdc1

Please sign in to comment.