Skip to content

Commit

Permalink
Expire Tenant API Tokens (#3479)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBitShepherd authored Dec 19, 2022
1 parent fb97dbc commit 20301bb
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 4 deletions.
2 changes: 2 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ def safe_key() -> str:
for net in env.list("ADMIN_API_ALLOWED_SUBNETS", default=["127.0.0.1/32"])
]

# API Token Stuff
API_TOKEN_EXPIRE_AFTER_DAYS = env.int("API_TOKEN_EXPIRE_AFTER_DAYS", default=30)

# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
Expand Down
3 changes: 2 additions & 1 deletion docs/scheduled_jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This job does four key things:
2. Delete any OAuth tokens older than 10 minutes if the user doesn't have any running jobs currently. This can be configured with the TOKEN_LIFETIME_MINUTES environment variable.
3. Deletes all non-staff users that have not logged in for the last thirty days.
4. Clears the exception field in `Job` and `Preflight` records over 90 days old. (This field may contain customer metadata such as custom schema names from the org).
5. Deletes any API tokens that are older than 30 days. The number of days can be configured with the `API_TOKEN_EXPIRE_AFTER_DAYS` environment variable.

## `expire_preflight_results`

Expand All @@ -23,4 +24,4 @@ Invalidates any preflight checks that were created more than 10 minutes ago. Thi

Frequency: daily

Calculates the average plan runtime for all plans, and then stores the value. Pages that need to reference plan runtime reference the calculated value for quicker page loads.
Calculates the average plan runtime for all plans, and then stores the value. Pages that need to reference plan runtime reference the calculated value for quicker page loads.
16 changes: 15 additions & 1 deletion metadeploy/api/cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from metadeploy.multitenancy import disable_site_filtering

from .models import Job, PreflightResult, User
from .models import Job, PreflightResult, User, Token
from .push import user_token_expired


Expand All @@ -25,6 +25,9 @@ def cleanup_user_data():
# remove job exceptions after 90 days
clear_old_exceptions()

# expire API access tokens after specified number of days
expire_api_access_tokens_older_than_days(settings.API_TOKEN_EXPIRE_AFTER_DAYS)


def expire_oauth_tokens():
"""Expire (delete) any SocialTokens older than TOKEN_LIFETIME_MINUTES.
Expand Down Expand Up @@ -100,3 +103,14 @@ def fix_dead_jobs_status():
Job.objects.filter(status="started", enqueued_at__lte=timeout_ago).update(
**canceled_values
)


@disable_site_filtering()
def expire_api_access_tokens_older_than_days(days: int):
"""Delete any Admin API access tokens older than days given.
We use @disable_site_filtering to ensure we query for tokens
across all tenants."""
obsolete_date = timezone.now() - timedelta(days=days)
expired_tokens = Token.objects.filter(created__lte=obsolete_date)
if expired_tokens:
expired_tokens.delete()
32 changes: 30 additions & 2 deletions metadeploy/api/tests/test_cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@
import pytest
from django.utils import timezone

from metadeploy.multitenancy import override_current_site_id

from metadeploy.multitenancy import (
override_current_site_id,
disable_site_filtering,
)

from ..cleanup import (
cleanup_user_data,
clear_old_exceptions,
delete_old_users,
expire_oauth_tokens,
expire_api_access_tokens_older_than_days,
fix_dead_jobs_status,
)
from ..models import User
from metadeploy.api.models import User, Token


@pytest.mark.django_db
Expand Down Expand Up @@ -120,3 +125,26 @@ def test_fix_dead_jobs_status__multi_tenancy(job_factory, extra_site):
assert (
old_job.status == "canceled"
), "Expected `fix_dead_job_status` to support Jobs from all Sites"


@pytest.mark.django_db
def test_expire_api_access_tokens(token_factory):
# set token creation time to two days ago
two_days_ago = timezone.now() - timedelta(days=2)
token = token_factory(created=two_days_ago)
token.created = two_days_ago
token.save()

# we should have 1 token to start with
with disable_site_filtering():
assert Token.objects.count() == 1

# token is 2 days old, so it won't be deleted
expire_api_access_tokens_older_than_days(3)
with disable_site_filtering():
assert Token.objects.count() == 1

# token should now be expired, and thus, deleted
expire_api_access_tokens_older_than_days(1)
with disable_site_filtering():
assert Token.objects.count() == 0

0 comments on commit 20301bb

Please sign in to comment.