Skip to content

Commit

Permalink
Merge branch 'apitoken-restrictions' into 'main'
Browse files Browse the repository at this point in the history
APIToken restrictions for community

See merge request reportcreator/reportcreator!486
  • Loading branch information
MWedl committed Mar 19, 2024
2 parents d0b9387 + 3f9e95e commit 2b58797
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 10 deletions.
4 changes: 2 additions & 2 deletions api/src/reportcreator_api/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
SECRET_KEY = config('SECRET_KEY', default='django-insecure-ygvn9(x==kcv#r%pccf4rlzyz7_1v1b83$19&b2lsj6uz$mbro')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False # config('DEBUG', cast=bool, default=False)
DEBUG = config('DEBUG', cast=bool, default=False)

ALLOWED_HOSTS = ['*']
APPEND_SLASH = True
Expand Down Expand Up @@ -581,7 +581,7 @@ def __bool__(self):
NOTIFICATION_IMPORT_URL = config('NOTIFICATION_IMPORT_URL', default='https://cloud.sysreptor.com/api/v1/notifications/')

# License
LICENSE = config('LICENSE', default=None)
LICENSE = None # TODO: config('LICENSE', default=None)
LICENSE_VALIDATION_KEYS = [
{'id': 'amber', 'algorithm': 'ed25519', 'key': 'MCowBQYDK2VwAyEAkqCS3lZbrzh+2mKTYymqPHtKBrh8glFxnj9OcoQR9xQ='},
{'id': 'silver', 'algorithm': 'ed25519', 'key': 'MCowBQYDK2VwAyEAwu/cl0CZSSBFOzFSz/hhUQQjHIKiT4RS3ekPevSKn7w='},
Expand Down
13 changes: 13 additions & 0 deletions api/src/reportcreator_api/tests/test_license.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from rest_framework import status
from django.utils.crypto import get_random_string

from reportcreator_api.users.models import APIToken
from reportcreator_api.utils import license
from reportcreator_api.tests.mock import create_project, create_project_type, create_public_key, create_template, create_user, api_client

Expand Down Expand Up @@ -167,6 +168,18 @@ def test_user_count_limit(self):
with pytest.raises(license.LicenseError):
self.user_regular.is_active = True
self.user_regular.save()

def test_apitoken_limit(self):
APIToken.objects.create(user=self.user_regular)

with pytest.raises(license.LicenseLimitExceededError):
APIToken.objects.create(user=self.user_regular)

def test_apitoken_no_expiry(self):
session = self.client.session
session.setdefault('authentication_info', {})['reauth_time'] = timezone.now().isoformat()
session.save()
assert_api_license_error(self.client.post(reverse('apitoken-list', kwargs={'pentestuser_pk': 'self'}), data={'name': 'test', 'expire_date': timezone.now().date().isoformat()}))


@pytest.mark.django_db
Expand Down
13 changes: 7 additions & 6 deletions api/src/reportcreator_api/users/querysets.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import secrets
from typing import Any
import pyotp
from fido2.server import Fido2Server, AttestedCredentialData
from fido2.webauthn import PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, UserVerificationRequirement, AuthenticatorAttachment, \
AttestationObject, CollectedClientData
from fido2.server import AttestedCredentialData
from fido2.webauthn import PublicKeyCredentialUserEntity, UserVerificationRequirement, AuthenticatorAttachment
from fido2.utils import websafe_encode, websafe_decode
from django.conf import settings
from django.db import models
from django.contrib.sessions.base_session import BaseSessionManager
from django.contrib.auth.models import UserManager
from django.utils.crypto import get_random_string
from django.utils import timezone


class SessionQueryset(models.QuerySet):
Expand Down Expand Up @@ -90,6 +87,10 @@ def only_permitted(self, user):
if user.is_admin or user.is_user_manager:
return self
return self.filter(user=user)

def only_active(self):
return self \
.filter(models.Q(expire_date=None) | models.Q(expire_date__lte=timezone.now().date()))


class APITokenManager(models.Manager.from_queryset(APITokenQuerySet)):
Expand Down
19 changes: 18 additions & 1 deletion api/src/reportcreator_api/users/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.dispatch import receiver
from django.conf import settings

from reportcreator_api.users.models import PentestUser
from reportcreator_api.users.models import APIToken, PentestUser
from reportcreator_api.utils import license


Expand Down Expand Up @@ -41,3 +41,20 @@ def user_count_license_check(sender, instance, *args, **kwargs):
if current_user_count + 1 > max_users:
raise license.LicenseError(f'License limit exceeded. Your license allows max. {max_users} users. Please deactivate some users or extend your license.')


@receiver(signals.pre_save, sender=APIToken)
def api_token_license_limit(sender, instance, *args, **kwargs):
if license.is_professional():
return

current_apitoken_count = APIToken.objects \
.filter(user=instance.user) \
.only_active() \
.count()
if current_apitoken_count >= 1:
raise license.LicenseLimitExceededError(
f'Community Edition allows max. 1 active API token per user. '
'Please delete some tokens or upgrade to Professional.')

if instance.expire_date:
raise license.LicenseError('API token expiration is not supported in Community edition.')
2 changes: 2 additions & 0 deletions frontend/src/components/S/DatePicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
@click:clear="emits('update:modelValue', null)"
v-bind="$attrs"
>
<template #label v-if="$slots.label"><slot name="label" /></template>

<v-menu
v-model="datePickerVisible"
:disabled="props.disabled || props.readonly"
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/users/self/apitokens.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,14 @@
label="Expire Date (optional)"
:error-messages="setupWizard.errors?.expire_date"
:min-date="formatISO9075(new Date(), { representation: 'date' })"
/>
:disabled="!apiSettings.isProfessionalLicense"
>
<template #label><pro-info>Expire Date (optional)</pro-info></template>
</s-date-picker>
<v-alert v-if="setupWizard.errors?.detail" color="error" class="mt-4">
{{ setupWizard.errors.detail }}
</v-alert>
</v-card-text>
<v-card-actions>
<v-spacer />
Expand Down Expand Up @@ -108,6 +115,8 @@
import { formatISO9075 } from 'date-fns';
const auth = useAuth();
const apiSettings = useApiSettings();
const apiTokens = await useAsyncDataE<ApiToken[]>(async () => {
try {
return await $fetch<ApiToken[]>('/api/v1/pentestusers/self/apitokens/', { method: 'GET' });
Expand Down

0 comments on commit 2b58797

Please sign in to comment.