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

API Token improvements - Token Rate Limits #27

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
108 changes: 0 additions & 108 deletions .taskcluster.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,11 @@ tasks:
then: ${event.pull_request.base.repo.clone_url}
else: ${event.repository.clone_url}

codecov_secret:
codecov-fuzzmanager

pypi_secret:
pypi-fuzzmanager

project_name:
FuzzManager

matrix:
language: python
secrets:
- type: env
secret: project/fuzzing/codecov-fuzzmanager
name: CODECOV_TOKEN
key: token
script:
- bash
- '-xec'
Expand Down Expand Up @@ -100,24 +89,6 @@ tasks:
npm run test;
npm run production;
npm run codecov;
- name: PyPI upload
version: "3.9"
env:
TOXENV: pypi
script:
- tox
when:
release: true
all_passed: true
secrets:
- type: env
secret: project/fuzzing/pypi-fuzzmanager
name: TWINE_USERNAME
key: username
- type: env
secret: project/fuzzing/pypi-fuzzmanager
name: TWINE_PASSWORD
key: password

in:
$if: >
Expand Down Expand Up @@ -152,88 +123,9 @@ tasks:
- queue:create-task:highest:proj-fuzzing/ci
- queue:create-task:highest:proj-fuzzing/ci-*
- queue:scheduler-id:taskcluster-github
- secrets:get:project/fuzzing/${codecov_secret}
- secrets:get:project/fuzzing/${pypi_secret}
metadata:
name: ${project_name} CI decision
description: Schedule CI tasks for ${project_name}
owner: '${user}@users.noreply.github.com'
source: ${http_repo}/raw/${fetch_rev}/.taskcluster.yml
- taskId: {$eval: as_slugid("docker")}
taskGroupId: ${task_group}
provisionerId: proj-fuzzing
workerType: ci
dependencies: []
created: {$fromNow: ''}
deadline: {$fromNow: '1 hour'}
payload:
image:
namespace: project.fuzzing.orion.orion-builder.master
path: public/orion-builder.tar.zst
type: indexed-image
maxRunTime: 3600
capabilities:
privileged: true
env:
LOAD_DEPS: "0"
GIT_REPOSITORY: ${http_repo}
GIT_REVISION: ${fetch_rev}
BUILD_TOOL: podman
DOCKERFILE: Dockerfile
IMAGE_NAME: mozillasecurity/fuzzmanager
ARCHIVE_PATH: /image.tar
command:
- sh
- -c
- uname -a && exec build
artifacts:
public/fuzzmanager.tar.zst:
expires: {$fromNow: '6 months'}
path: /image.tar.zst
type: file
scopes:
- docker-worker:capability:privileged
metadata:
name: FuzzManager Docker build
description: FuzzManager Docker build
owner: '${user}@users.noreply.github.com'
source: ${http_repo}/raw/${fetch_rev}/.taskcluster.yml
- $if: 'tasks_for in ["github-push"] && fetch_ref == "refs/heads/master"'
then:
taskId: {$eval: as_slugid("docker_push")}
taskGroupId: ${task_group}
provisionerId: proj-fuzzing
workerType: ci
dependencies:
- {$eval: as_slugid("docker")}
created: {$fromNow: ''}
deadline: {$fromNow: '1 hour'}
payload:
capabilities:
privileged: true
image:
namespace: project.fuzzing.orion.orion-builder.master
path: public/orion-builder.tar.zst
type: indexed-image
maxRunTime: 3600
features:
taskclusterProxy: true
env:
TASK_ID: {$eval: as_slugid("docker")}
TASKCLUSTER_SECRET: "project/fuzzing/docker-hub"
GIT_REPOSITORY: ${http_repo}
GIT_REVISION: ${fetch_rev}
SERVICE_NAME: fuzzmanager
command:
- sh
- -c
- uname -a && exec push
scopes:
- docker-worker:capability:privileged
- secrets:get:project/fuzzing/docker-hub
metadata:
name: FuzzManager Docker push
description: FuzzManager Docker push
owner: '${user}@users.noreply.github.com'
source: ${http_repo}/raw/${fetch_rev}/.taskcluster.yml
else: []
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ USER node
WORKDIR /src

RUN npm install
RUN npm run build
RUN npm run production

FROM python:3.10-alpine as backend

Expand Down
6 changes: 5 additions & 1 deletion server/covmanager/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.views.decorators.csrf import csrf_exempt
from rest_framework import filters, mixins, viewsets
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework.throttling import UserRateThrottle

from crashmanager.models import Tool
from server.views import JsonQueryFilterBackend, SimpleQueryFilterBackend
Expand All @@ -25,7 +26,6 @@
from .SourceCodeProvider import SourceCodeProvider
from .tasks import aggregate_coverage_data, calculate_report_summary


def index(request):
return redirect(
f"covmanager:{getattr(settings, 'COV_DEFAULT_PAGE', 'collections')}"
Expand Down Expand Up @@ -705,6 +705,7 @@ class CollectionViewSet(
API endpoint that allows adding/viewing Collections
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = Collection.objects.all()
serializer_class = CollectionSerializer
Expand Down Expand Up @@ -754,6 +755,7 @@ class ReportViewSet(
API endpoint that allows viewing Reports
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = Report.objects.all()
serializer_class = ReportSerializer
Expand All @@ -778,6 +780,7 @@ class RepositoryViewSet(
API endpoint that allows viewing Repositories
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication,)
queryset = Repository.objects.all()
serializer_class = RepositorySerializer
Expand Down Expand Up @@ -819,6 +822,7 @@ class ReportConfigurationViewSet(
API endpoint that allows adding/updating/viewing Report Configurations
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = ReportConfiguration.objects.all()
serializer_class = ReportConfigurationSerializer
Expand Down
10 changes: 10 additions & 0 deletions server/crashmanager/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from rest_framework.filters import BaseFilterBackend, OrderingFilter
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.throttling import UserRateThrottle

from FTB.ProgramConfiguration import ProgramConfiguration
from FTB.Signatures.CrashInfo import CrashInfo
Expand Down Expand Up @@ -1017,6 +1018,7 @@ class CrashEntryViewSet(
):
"""API endpoint that allows adding/viewing CrashEntries"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = CrashEntry.objects.all().select_related(
"product", "platform", "os", "client", "tool", "testcase"
Expand Down Expand Up @@ -1143,6 +1145,7 @@ class BucketViewSet(
):
"""API endpoint that allows viewing Buckets"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = Bucket.objects.all().select_related("bug", "bug__externalType")
serializer_class = BucketSerializer
Expand Down Expand Up @@ -1420,6 +1423,8 @@ def create(self, request, *args, **kwargs):
class BucketVueViewSet(BucketViewSet):
"""API endpoint that allows viewing Buckets and always uses Vue serializer"""

throttle_classes = [UserRateThrottle]

def get_serializer(self, *args, **kwds):
self.vue = True
return BucketVueSerializer(*args, **kwds)
Expand All @@ -1430,6 +1435,7 @@ class BugProviderViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
API endpoint that allows listing BugProviders
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = BugProvider.objects.all()
serializer_class = BugProviderSerializer
Expand All @@ -1442,6 +1448,7 @@ class BugzillaTemplateViewSet(
API endpoint that allows viewing BugzillaTemplates
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = BugzillaTemplate.objects.all()
serializer_class = BugzillaTemplateSerializer
Expand All @@ -1452,6 +1459,7 @@ class NotificationViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
API endpoint that allows listing unread Notifications
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
serializer_class = NotificationSerializer
filter_backends = [
Expand Down Expand Up @@ -1545,6 +1553,7 @@ def get_query_obj(obj, key=None):


class AbstractDownloadView(APIView):
throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (CheckAppPermission,)

Expand Down Expand Up @@ -1765,6 +1774,7 @@ class CrashStatsViewSet(viewsets.GenericViewSet):
API endpoint that allows retrieving CrashManager statistics
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication, SessionAuthentication)
queryset = CrashEntry.objects.all()
filter_backends = [
Expand Down
6 changes: 6 additions & 0 deletions server/ec2spotmanager/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.throttling import UserRateThrottle

from server.auth import CheckAppPermission

Expand Down Expand Up @@ -917,6 +918,7 @@ def get_labels(self, pool, entries):


class MachineStatusViewSet(APIView):
throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication,)

def get(self, request, *args, **kwargs):
Expand Down Expand Up @@ -947,6 +949,7 @@ class PoolConfigurationViewSet(
API endpoint that allows viewing PoolConfigurations
"""

throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication,)
permission_classes = (CheckAppPermission,)
queryset = PoolConfiguration.objects.all()
Expand All @@ -964,6 +967,7 @@ def retrieve(self, request, *args, **kwds):


class PoolCycleView(APIView):
throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication,)
permission_classes = (CheckAppPermission,)

Expand All @@ -982,6 +986,7 @@ def post(self, request, poolid, format=None):


class PoolEnableView(APIView):
throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication,)
permission_classes = (CheckAppPermission,)

Expand All @@ -1002,6 +1007,7 @@ def post(self, request, poolid, format=None):


class PoolDisableView(APIView):
throttle_classes = [UserRateThrottle]
authentication_classes = (TokenAuthentication,)
permission_classes = (CheckAppPermission,)

Expand Down
9 changes: 9 additions & 0 deletions server/server/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ def resolver_context_processor(request):
"DEFAULT_PERMISSION_CLASSES": ("server.auth.CheckAppPermission",),
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"PAGE_SIZE": 100,
"DEFAULT_THROTTLE_RATES": {"user": "100/minute"},
}

# Logging
Expand Down Expand Up @@ -425,3 +426,11 @@ def resolver_context_processor(request):

# Report coverage reports with a drop of greater than 10%
COVERAGE_REPORT_DELTA = 10

# Setup cache settings
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": REDIS_URL,
}
}
25 changes: 25 additions & 0 deletions server/server/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Common utilities for tests

@license:

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
import logging

import pytest
from django.contrib.auth.models import User
from django.test import Client
from rest_framework.authtoken.models import Token

LOG = logging.getLogger("fm.server.tests")


@pytest.fixture
def auth_client():
"""Create an authenticated client with token"""
client = Client()
user = User.objects.create_superuser("test", "[email protected]", "test")
token = Token.objects.create(user=user)
return client, token
Loading