mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-12 19:39:35 -06:00
Enforce a fixed key length for v2 tokens
This commit is contained in:
parent
1ee23ba6fa
commit
5dc48f3a88
@ -10,4 +10,7 @@ OBJECTPERMISSION_OBJECT_TYPES = Q(
|
||||
|
||||
CONSTRAINT_TOKEN_USER = '$user'
|
||||
|
||||
# API tokens
|
||||
TOKEN_KEY_LENGTH = 16
|
||||
TOKEN_DEFAULT_LENGTH = 40
|
||||
TOKEN_CHARSET = string.ascii_letters + string.digits
|
||||
|
||||
@ -21,6 +21,7 @@ class Migration(migrations.Migration):
|
||||
migrations.RunSQL(
|
||||
sql="ALTER INDEX IF EXISTS users_token_key_key RENAME TO users_token_plaintext_key",
|
||||
),
|
||||
|
||||
# Make plaintext (formerly key) nullable for v2 tokens
|
||||
migrations.AlterField(
|
||||
model_name='token',
|
||||
@ -33,6 +34,7 @@ class Migration(migrations.Migration):
|
||||
validators=[django.core.validators.MinLengthValidator(40)]
|
||||
),
|
||||
),
|
||||
|
||||
# Add version field to distinguish v1 and v2 tokens
|
||||
migrations.AddField(
|
||||
model_name='token',
|
||||
@ -40,17 +42,25 @@ class Migration(migrations.Migration):
|
||||
field=models.PositiveSmallIntegerField(default=1), # Mark all existing Tokens as v1
|
||||
preserve_default=False,
|
||||
),
|
||||
|
||||
# Change the default version for new tokens to v2
|
||||
migrations.AlterField(
|
||||
model_name='token',
|
||||
name='version',
|
||||
field=models.PositiveSmallIntegerField(default=2),
|
||||
),
|
||||
|
||||
# Add new key, pepper, and hmac_digest fields for v2 tokens
|
||||
migrations.AddField(
|
||||
model_name='token',
|
||||
name='key',
|
||||
field=models.CharField(blank=True, max_length=16, null=True, unique=True),
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
max_length=16,
|
||||
null=True,
|
||||
unique=True,
|
||||
validators=[django.core.validators.MinLengthValidator(16)]
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='token',
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import binascii
|
||||
import hashlib
|
||||
import hmac
|
||||
import random
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
@ -16,7 +14,7 @@ from netaddr import IPNetwork
|
||||
|
||||
from ipam.fields import IPNetworkField
|
||||
from users.choices import TokenVersionChoices
|
||||
from users.constants import TOKEN_CHARSET
|
||||
from users.constants import TOKEN_CHARSET, TOKEN_DEFAULT_LENGTH, TOKEN_KEY_LENGTH
|
||||
from users.utils import get_current_pepper
|
||||
from utilities.querysets import RestrictedQuerySet
|
||||
|
||||
@ -75,10 +73,11 @@ class Token(models.Model):
|
||||
)
|
||||
key = models.CharField(
|
||||
verbose_name=_('key'),
|
||||
max_length=16,
|
||||
max_length=TOKEN_KEY_LENGTH,
|
||||
unique=True,
|
||||
blank=True,
|
||||
null=True,
|
||||
validators=[MinLengthValidator(TOKEN_KEY_LENGTH)],
|
||||
help_text=_('v2 token identification key'),
|
||||
)
|
||||
pepper = models.PositiveSmallIntegerField(
|
||||
@ -148,7 +147,7 @@ class Token(models.Model):
|
||||
if self.v1:
|
||||
self.plaintext = value
|
||||
elif self.v2:
|
||||
self.key = self.key or self.generate(16)
|
||||
self.key = self.key or self.generate_key()
|
||||
self.update_digest()
|
||||
|
||||
def clean(self):
|
||||
@ -162,15 +161,15 @@ class Token(models.Model):
|
||||
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def generate_key():
|
||||
@classmethod
|
||||
def generate_key(cls):
|
||||
"""
|
||||
DEPRECATED: Generate and return a random 160-bit key expressed in hexadecimal.
|
||||
Generate and return a random alphanumeric key for v2 tokens.
|
||||
"""
|
||||
return binascii.hexlify(os.urandom(20)).decode()
|
||||
return cls.generate(length=TOKEN_KEY_LENGTH)
|
||||
|
||||
@staticmethod
|
||||
def generate(length=40):
|
||||
def generate(length=TOKEN_DEFAULT_LENGTH):
|
||||
"""
|
||||
Generate and return a random token value of the given length.
|
||||
"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user