misc: prepare removing nonce application (#40685)
Remove all code and models.
This commit is contained in:
parent
785e28d93a
commit
b2f6688190
|
@ -32,7 +32,6 @@ from django.views.decorators.cache import never_cache
|
|||
from . import app_settings, attribute_kinds, decorators, models
|
||||
from .custom_user.models import DeletedUser, Profile, ProfileType, User
|
||||
from .forms.profile import BaseUserForm, modelform_factory
|
||||
from .nonce.models import Nonce
|
||||
from .utils import misc as utils_misc
|
||||
|
||||
|
||||
|
@ -49,11 +48,6 @@ class CleanupAdminMixin(admin.ModelAdmin):
|
|||
return actions
|
||||
|
||||
|
||||
@admin.register(Nonce)
|
||||
class NonceModelAdmin(admin.ModelAdmin):
|
||||
list_display = ('value', 'context', 'not_on_or_after')
|
||||
|
||||
|
||||
class AttributeValueAdmin(admin.ModelAdmin):
|
||||
list_display = ('content_type', 'owner', 'attribute', 'content')
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
Nonce are value which should be used only once for a certain period or
|
||||
eventually forever. The nonce application allows any Django application to
|
||||
implement this behaviour, by taking care of the storage implementation to keep around invalidated nonce.
|
||||
|
||||
For nonce which should not be kept forever the application also provide a
|
||||
cleanup_nonce() function to delete the no longer invalid nonces.
|
|
@ -1,19 +0,0 @@
|
|||
# authentic2 - versatile identity manager
|
||||
# Copyright (C) 2010-2019 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from authentic2.nonce.utils import accept_nonce, cleanup_nonces
|
||||
|
||||
__all__ = ('accept_nonce', 'cleanup_nonces')
|
|
@ -0,0 +1,13 @@
|
|||
# Generated by Django 3.2.18 on 2023-12-13 20:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
replaces = [('nonce', '0001_initial'), ('nonce', '0002_delete_nonce')]
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = []
|
|
@ -0,0 +1,15 @@
|
|||
# Generated by Django 3.2.18 on 2023-12-13 19:03
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('nonce', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='Nonce',
|
||||
),
|
||||
]
|
|
@ -1,39 +0,0 @@
|
|||
# authentic2 - versatile identity manager
|
||||
# Copyright (C) 2010-2019 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
__all__ = ('Nonce',)
|
||||
|
||||
_NONCE_LENGTH_CONSTANT = 256
|
||||
|
||||
|
||||
class NonceManager(models.Manager):
|
||||
def cleanup(self, now=None):
|
||||
now = now or timezone.now()
|
||||
self.filter(not_on_or_after__lt=now).delete()
|
||||
|
||||
|
||||
class Nonce(models.Model):
|
||||
value = models.CharField(max_length=_NONCE_LENGTH_CONSTANT)
|
||||
context = models.CharField(max_length=_NONCE_LENGTH_CONSTANT, blank=True, null=True)
|
||||
not_on_or_after = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
objects = NonceManager()
|
||||
|
||||
def __str__(self):
|
||||
return str(self.value)
|
|
@ -1,200 +0,0 @@
|
|||
# authentic2 - versatile identity manager
|
||||
# Copyright (C) 2010-2019 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime as dt
|
||||
import errno
|
||||
import glob
|
||||
import os.path
|
||||
import tempfile
|
||||
from calendar import timegm
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
__all__ = ('accept_nonce', 'cleanup_nonces')
|
||||
|
||||
STORAGE_MODEL = 'model'
|
||||
STORAGE_FILESYSTEM = 'fs:'
|
||||
|
||||
|
||||
def compute_not_on_or_after(now, not_on_or_after):
|
||||
try: # first try integer semantic
|
||||
seconds = int(not_on_or_after)
|
||||
not_on_or_after = now + dt.timedelta(seconds=seconds)
|
||||
except ValueError:
|
||||
try: # try timedelta semantic
|
||||
not_on_or_after = now + not_on_or_after
|
||||
except TypeError: # datetime semantic
|
||||
pass
|
||||
return not_on_or_after
|
||||
|
||||
|
||||
# For the nonce filesystem storage the policy is to catch any OSerror when
|
||||
# errno == ENOENT and to handle this case gracefully as there can be many race
|
||||
# condition errors. But any other OSError is problematic and should be
|
||||
# reported to the administrator by mail and so we let it unroll the stack
|
||||
|
||||
|
||||
def unlink_if_exists(path):
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError as e:
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
|
||||
|
||||
def accept_nonce_file_storage(path, now, value, context=None, not_on_or_after=None):
|
||||
"""
|
||||
Use a directory as a storage for nonce-context values. The last
|
||||
modification time is used to store the expiration timestamp.
|
||||
"""
|
||||
now = dt.datetime.now()
|
||||
filename = '%s_%s' % (value.encode('base64'), context.encode('base64'))
|
||||
file_path = os.path.join(path, filename)
|
||||
# test if the file exists
|
||||
try:
|
||||
stat = os.stat(file_path)
|
||||
except OSError as e:
|
||||
# test if it doesnt exit or if another error happened
|
||||
if e.errno != errno.ENOENT:
|
||||
raise
|
||||
else:
|
||||
# if it exists, test if it is too old
|
||||
if stat.st_mtime < timegm(now.utctimetuple()):
|
||||
# if it is too old delete it
|
||||
unlink_if_exists(file_path)
|
||||
else:
|
||||
# not too old, the nonce is unacceptable
|
||||
return False
|
||||
|
||||
# pylint: disable=consider-using-with
|
||||
temp_file = tempfile.NamedTemporaryFile(dir=path, delete=False)
|
||||
temp_file.close()
|
||||
|
||||
if not_on_or_after:
|
||||
not_on_or_after = compute_not_on_or_after(now, not_on_or_after)
|
||||
mtime = timegm(not_on_or_after.utctimetuple())
|
||||
else:
|
||||
mtime = 0x7FFF
|
||||
try:
|
||||
os.utime(temp_file.name, (mtime, mtime))
|
||||
except OSError:
|
||||
unlink_if_exists(temp_file.name)
|
||||
raise
|
||||
try:
|
||||
os.link(temp_file.name, file_path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
unlink_if_exists(temp_file.name)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def accept_nonce_model(now, value, context=None, not_on_or_after=None):
|
||||
from authentic2.nonce.models import Nonce
|
||||
|
||||
if not_on_or_after:
|
||||
not_on_or_after = compute_not_on_or_after(now, not_on_or_after)
|
||||
nonce, created = Nonce.objects.get_or_create(value=value, context=context)
|
||||
if created or (nonce.not_on_or_after and nonce.not_on_or_after < now):
|
||||
nonce.not_on_or_after = not_on_or_after
|
||||
nonce.save()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def cleanup_nonces_file_storage(dir_path, now):
|
||||
for nonce_path in glob.iglob(os.path.join(dir_path, '*')):
|
||||
now_time = timegm(now.utctimetuple())
|
||||
try:
|
||||
stat = os.stat(nonce_path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
continue
|
||||
raise
|
||||
if stat.st_mtime < now_time:
|
||||
try:
|
||||
os.unlink(nonce_path)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
continue
|
||||
raise
|
||||
|
||||
|
||||
def cleanup_nonces(now=None):
|
||||
"""
|
||||
Cleanup stored nonce whose timestamp has expired, i.e.
|
||||
nonce.not_on_or_after < now.
|
||||
|
||||
:param now:
|
||||
a datetime value to define what is the current time, if None is
|
||||
given, datetime.now() is used. It can be used for unit testing.
|
||||
"""
|
||||
from authentic2.nonce.models import Nonce
|
||||
|
||||
now = now or dt.datetime.now()
|
||||
mode = getattr(settings, 'NONCE_STORAGE', STORAGE_MODEL)
|
||||
# the model always exists, so we always clean it
|
||||
Nonce.objects.cleanup(now)
|
||||
if mode == STORAGE_MODEL:
|
||||
pass
|
||||
if mode.startswith(STORAGE_FILESYSTEM):
|
||||
dir_path = mode[len(STORAGE_FILESYSTEM) :]
|
||||
return cleanup_nonces_file_storage(dir_path, now)
|
||||
else:
|
||||
raise ValueError('Invalid NONCE_STORAGE setting: %r' % mode)
|
||||
|
||||
|
||||
def accept_nonce(value, context=None, not_on_or_after=None, now=None):
|
||||
"""
|
||||
Verify that the given nonce value has not already been seen in the
|
||||
context. If not, remember it for ever or until not_on_or_after if is
|
||||
not None.
|
||||
|
||||
Depending on the backend storage used there can be limitation on the
|
||||
acceptable length for the value and the context. For example the model
|
||||
storage backend limits the length of those strings to 256 bytes.
|
||||
|
||||
:param value:
|
||||
a string representing a nonce value.
|
||||
:param context:
|
||||
a string giving context to the nonce value
|
||||
:param not_on_or_after:
|
||||
an integer, a datetime.timedelta or datetime.datetime value. If not
|
||||
none it is used to compute the expiration time for remembering this
|
||||
nonce value. If an integer is given it is interpreted as relative
|
||||
number of seconds since now, if a timedelta object is given it is
|
||||
used as an offset from now, and if a datetime is given it is used
|
||||
as an absolute value for the expiration time.
|
||||
:param now:
|
||||
a datetime value to define what is the current time, if None is
|
||||
given, datetime.now() is used. It can be used for unit testing.
|
||||
:returns:
|
||||
a boolean, if True the nonce has never been seen before, or it
|
||||
expired since the last time seen, otherwise the nonce has already
|
||||
been seen and is invalid.
|
||||
"""
|
||||
now = now or dt.datetime.now()
|
||||
mode = getattr(settings, 'NONCE_STORAGE', STORAGE_MODEL)
|
||||
if mode == STORAGE_MODEL:
|
||||
return accept_nonce_model(now, value, context=context, not_on_or_after=not_on_or_after)
|
||||
elif mode.startswith(STORAGE_FILESYSTEM):
|
||||
dir_path = mode[len(STORAGE_FILESYSTEM) :]
|
||||
return accept_nonce_file_storage(
|
||||
dir_path, now, value, context=context, not_on_or_after=not_on_or_after
|
||||
)
|
||||
else:
|
||||
raise ValueError('Invalid NONCE_STORAGE setting: %r' % mode)
|
Loading…
Reference in New Issue