remove old api secret
This commit is contained in:
parent
5c203a7a2b
commit
cf765acc00
6 changed files with 46 additions and 22 deletions
|
@ -2,13 +2,20 @@ import string
|
|||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import constant_time_compare, get_random_string
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class SecretQuerySet(models.QuerySet):
|
||||
def get_by_secret(self, secret):
|
||||
self.filter(secret=secret, )
|
||||
return self.filter(api_secret=secret).valid_only()
|
||||
|
||||
def valid_only(self):
|
||||
return self.filter(
|
||||
Q(valid_until__isnull=True) | Q(valid_until__gte=timezone.now()),
|
||||
)
|
||||
|
||||
|
||||
class Secret(models.Model):
|
||||
|
@ -22,6 +29,8 @@ class Secret(models.Model):
|
|||
scope_mesh = models.BooleanField(_('mesh access'), default=False)
|
||||
valid_until = models.DateTimeField(null=True, verbose_name=_('valid_until'))
|
||||
|
||||
objects = models.Manager.from_queryset(SecretQuerySet)()
|
||||
|
||||
def scopes_display(self):
|
||||
return [
|
||||
field.verbose_name for field in self._meta.get_fields()
|
||||
|
|
|
@ -5,8 +5,6 @@ from importlib import import_module
|
|||
|
||||
from django.contrib.auth import get_user as auth_get_user
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import SimpleLazyObject, lazy
|
||||
from ninja.security import HttpBearer
|
||||
|
||||
|
@ -77,10 +75,7 @@ class APITokenAuth(HttpBearer):
|
|||
)
|
||||
elif token.startswith("secret:"):
|
||||
try:
|
||||
secret = Secret.objects.filter(
|
||||
Q(api_secret=token.removeprefix("secret:")),
|
||||
Q(valid_until__isnull=True) | Q(valid_until__gte=timezone.now()),
|
||||
).select_related("user", "user__permissions").get()
|
||||
secret = Secret.objects.get_by_secret(token.removeprefix("secret:")).get()
|
||||
except Secret.DoesNotExist:
|
||||
raise APITokenInvalid
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ from django.utils import timezone
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
from c3nav.api.models import Secret
|
||||
from c3nav.control.models import UserPermissions, UserSpaceAccess
|
||||
from c3nav.mapdata.forms import I18nModelFormMixin
|
||||
from c3nav.mapdata.models import MapUpdate, Space
|
||||
|
@ -159,8 +160,10 @@ class AccessPermissionForm(Form):
|
|||
|
||||
def get_signed_data(self, key=None):
|
||||
# todo: yep, we stil need to fix this
|
||||
if not self.author.permissions.api_secret:
|
||||
raise ValueError('Author has no api secret.')
|
||||
try:
|
||||
api_secret = self.author.api_secrets.filter(scope_grant_permission=True).valid_only().get().api_secret
|
||||
except Secret.DoesNotExist:
|
||||
raise ValueError('Author has no feasable api secret.')
|
||||
data = {
|
||||
'id': self.data['access_restrictions'],
|
||||
'time': int(time.time()),
|
||||
|
@ -170,8 +173,7 @@ class AccessPermissionForm(Form):
|
|||
if key is not None:
|
||||
data['key'] = key
|
||||
data = json.dumps(data, separators=(',', ':'))
|
||||
signature = hmac.new(self.author.permissions.api_secret.encode(),
|
||||
msg=data.encode(), digestmod=hashlib.sha256).digest()
|
||||
signature = hmac.new(api_secret, msg=data.encode(), digestmod=hashlib.sha256).digest()
|
||||
return '%s:%s' % (data, binascii.b2a_base64(signature).strip().decode())
|
||||
|
||||
@classmethod
|
||||
|
@ -230,16 +232,19 @@ class AccessPermissionForm(Form):
|
|||
except User.DoesNotExist:
|
||||
raise SignedPermissionDataError('Author does not exist.')
|
||||
|
||||
try:
|
||||
api_secret = author.permissions.api_secret
|
||||
except AttributeError:
|
||||
api_secrets = author.api_secrets.filter(
|
||||
scope_grant_permission=True
|
||||
).valid_only().values_list('api_secret', flat=True)
|
||||
if not api_secrets:
|
||||
raise SignedPermissionDataError('Author has no API secret.')
|
||||
|
||||
verify_signature = binascii.b2a_base64(hmac.new(api_secret.encode(),
|
||||
msg=raw_data.encode(), digestmod=hashlib.sha256).digest())
|
||||
print(verify_signature, signature)
|
||||
if signature != verify_signature.strip().decode():
|
||||
raise SignedPermissionDataError('Invalid signature.')
|
||||
for api_secret in api_secrets:
|
||||
verify_signature = binascii.b2a_base64(hmac.new(api_secret.encode(),
|
||||
msg=raw_data.encode(), digestmod=hashlib.sha256).digest())
|
||||
if signature == verify_signature.strip().decode():
|
||||
break
|
||||
else:
|
||||
raise SignedPermissionDataError('Invalid signature.') # todo: test this!!
|
||||
|
||||
form = cls(author=author, expire_date=valid_until, data={
|
||||
'access_restrictions': str(restrictions),
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.3 on 2023-12-01 16:38
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("control", "0010_userpermissions_mesh_control"),
|
||||
("api", "0003_rename_token_logintoken_secret"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="userpermissions",
|
||||
name="api_secret",
|
||||
),
|
||||
]
|
|
@ -37,8 +37,6 @@ class UserPermissions(models.Model):
|
|||
|
||||
mesh_control = models.BooleanField(default=False, verbose_name=_('can access mesh control'))
|
||||
|
||||
api_secret = models.CharField(null=True, blank=True, max_length=64, verbose_name=_('API secret'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('User Permissions')
|
||||
verbose_name_plural = _('User Permissions')
|
||||
|
|
|
@ -22,7 +22,7 @@ def grant_access(request): # todo: make class based view
|
|||
token = form.get_token()
|
||||
token.save()
|
||||
# todo: this still needs fixing
|
||||
if settings.DEBUG and request.user_permissions.api_secret:
|
||||
if settings.DEBUG:
|
||||
signed_data = form.get_signed_data()
|
||||
print('/?'+urlencode({'access': signed_data}))
|
||||
return redirect(reverse('control.access.qr', kwargs={'token': token.token}))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue