trivial: apply black
This commit is contained in:
parent
ed779e948e
commit
faa1f97b00
|
@ -14,15 +14,15 @@
|
|||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
#ADMINS = (
|
||||
# ADMINS = (
|
||||
# # ('User 1', 'watchdog@example.net'),
|
||||
# # ('User 2', 'janitor@example.net'),
|
||||
#)
|
||||
# )
|
||||
|
||||
# ALLOWED_HOSTS must be correct in production!
|
||||
# See https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = [
|
||||
'*',
|
||||
'*',
|
||||
]
|
||||
|
||||
# Databases
|
||||
|
|
|
@ -56,11 +56,18 @@ class UserDocumentAdmin(admin.ModelAdmin):
|
|||
list_display = ['user', 'filename', 'thumbnail', 'created', 'origin']
|
||||
fields = ['id', 'user', 'filename', 'thumbnail', 'created', 'origin']
|
||||
readonly_fields = ['created', 'thumbnail']
|
||||
search_fields = ['user__first_name', 'user__last_name', 'user__email', 'filename',
|
||||
'origin__label', 'document__content_hash']
|
||||
search_fields = [
|
||||
'user__first_name',
|
||||
'user__last_name',
|
||||
'user__email',
|
||||
'filename',
|
||||
'origin__label',
|
||||
'document__content_hash',
|
||||
]
|
||||
|
||||
def thumbnail(self, instance):
|
||||
return instance.document.thumbnail_img_tag
|
||||
|
||||
thumbnail.short_description = _('thumbnail')
|
||||
|
||||
|
||||
|
@ -77,22 +84,14 @@ class DocumentAdmin(admin.ModelAdmin):
|
|||
|
||||
def thumbnail(self, instance):
|
||||
return instance.thumbnail_img_tag
|
||||
|
||||
thumbnail.short_description = _('thumbnail')
|
||||
|
||||
|
||||
class ValidationAdmin(admin.ModelAdmin):
|
||||
fields = ['user',
|
||||
'content_hash',
|
||||
'document_type',
|
||||
'start',
|
||||
'end',
|
||||
'data',
|
||||
'creator',
|
||||
'created',
|
||||
'origin']
|
||||
fields = ['user', 'content_hash', 'document_type', 'start', 'end', 'data', 'creator', 'created', 'origin']
|
||||
readonly_fields = ['created']
|
||||
list_display = ['user', 'display', 'document_type', 'start', 'end', 'creator', 'created',
|
||||
'origin']
|
||||
list_display = ['user', 'display', 'document_type', 'start', 'end', 'creator', 'created', 'origin']
|
||||
|
||||
|
||||
class OriginAdmin(admin.ModelAdmin):
|
||||
|
|
|
@ -97,8 +97,10 @@ class PushDocumentSerializer(UserSerializerMixin):
|
|||
def validate(self, data):
|
||||
data = super(PushDocumentSerializer, self).validate(data)
|
||||
user = data['user']
|
||||
if (Document.objects.used_space(user) + data['file_b64_content'].size
|
||||
> settings.FARGO_MAX_DOCUMENT_BOX_SIZE):
|
||||
if (
|
||||
Document.objects.used_space(user) + data['file_b64_content'].size
|
||||
> settings.FARGO_MAX_DOCUMENT_BOX_SIZE
|
||||
):
|
||||
raise api_errors.APIError('BOX_IS_FULL', limit=str(settings.FARGO_MAX_DOCUMENT_BOX_SIZE))
|
||||
return data
|
||||
|
||||
|
@ -115,23 +117,24 @@ class PushDocument(CommonAPIMixin, GenericAPIView):
|
|||
data = serializer.validated_data
|
||||
|
||||
origin, created = Origin.objects.get_or_create(
|
||||
slug=slugify(data.get('origin')),
|
||||
defaults={'label': data.get('origin')})
|
||||
slug=slugify(data.get('origin')), defaults={'label': data.get('origin')}
|
||||
)
|
||||
|
||||
document_file = data['file_b64_content']
|
||||
if data.get('file_name'):
|
||||
document_file.name = data.get('file_name')
|
||||
content_hash = utils.sha256_of_file(document_file)
|
||||
document, created = Document.objects.get_or_create(
|
||||
content_hash=content_hash,
|
||||
defaults={'content': document_file})
|
||||
content_hash=content_hash, defaults={'content': document_file}
|
||||
)
|
||||
|
||||
user_document, created = UserDocument.objects.get_or_create(
|
||||
user=data.get('user'),
|
||||
filename=data.get('file_name'),
|
||||
document=document,
|
||||
origin=origin,
|
||||
deletable_by_user=data.get('deletable_by_user'))
|
||||
deletable_by_user=data.get('deletable_by_user'),
|
||||
)
|
||||
if not created:
|
||||
raise api_errors.APIError('DOCUMENT_EXISTS')
|
||||
user_document.save()
|
||||
|
@ -139,6 +142,7 @@ class PushDocument(CommonAPIMixin, GenericAPIView):
|
|||
response_status = status.HTTP_200_OK
|
||||
return Response(None, response_status)
|
||||
|
||||
|
||||
push_document = PushDocument.as_view()
|
||||
|
||||
|
||||
|
@ -155,10 +159,10 @@ class RecentDocuments(ListAPIView):
|
|||
|
||||
def get_queryset(self):
|
||||
return UserDocument.objects.filter(
|
||||
user=self.request.user,
|
||||
created__gt=datetime.datetime.now() - datetime.timedelta(days=14)
|
||||
user=self.request.user, created__gt=datetime.datetime.now() - datetime.timedelta(days=14)
|
||||
).order_by('-created')[:10]
|
||||
|
||||
|
||||
recent_documents = RecentDocuments.as_view()
|
||||
|
||||
|
||||
|
@ -176,11 +180,13 @@ class ValidationSerializer(UserSerializerMixin, serializers.ModelSerializer):
|
|||
name = field['varname']
|
||||
required = field.get('required', True)
|
||||
self.fields[name] = serializers.CharField(
|
||||
source='data.%s' % name, required=required, allow_blank=True)
|
||||
source='data.%s' % name, required=required, allow_blank=True
|
||||
)
|
||||
|
||||
def get_url(self, instance):
|
||||
url = reverse('fargo-api-validation-detail',
|
||||
kwargs={'document_type': instance.document_type, 'pk': instance.pk})
|
||||
url = reverse(
|
||||
'fargo-api-validation-detail', kwargs={'document_type': instance.document_type, 'pk': instance.pk}
|
||||
)
|
||||
if 'request' in self.context:
|
||||
url = self.context['request'].build_absolute_uri(url)
|
||||
return url
|
||||
|
@ -209,11 +215,13 @@ class FilterByUser(filters.BaseFilterBackend):
|
|||
return queryset
|
||||
|
||||
|
||||
class ValidationAPI(CommonAPIMixin,
|
||||
mixins.CreateModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
viewsets.GenericViewSet):
|
||||
class ValidationAPI(
|
||||
CommonAPIMixin,
|
||||
mixins.CreateModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
viewsets.GenericViewSet,
|
||||
):
|
||||
serializer_class = ValidationSerializer
|
||||
permission_classes = (IsAdminUser,)
|
||||
filter_backends = [FilterByUser]
|
||||
|
@ -235,6 +243,6 @@ class ValidationAPI(CommonAPIMixin,
|
|||
# pass schema to serializer class
|
||||
return super(ValidationAPI, self).get_serializer(schema=self.document_type_schema, *args, **kwargs)
|
||||
|
||||
|
||||
router = routers.SimpleRouter()
|
||||
router.register(r'validation/(?P<document_type>[^/]*)', ValidationAPI,
|
||||
base_name='fargo-api-validation')
|
||||
router.register(r'validation/(?P<document_type>[^/]*)', ValidationAPI, base_name='fargo-api-validation')
|
||||
|
|
|
@ -24,30 +24,33 @@ from . import models
|
|||
|
||||
|
||||
class UploadForm(forms.ModelForm):
|
||||
content = forms.FileField(
|
||||
label=_('file'), max_length=512)
|
||||
content = forms.FileField(label=_('file'), max_length=512)
|
||||
|
||||
def clean_content(self):
|
||||
content = self.cleaned_data.get('content')
|
||||
if content:
|
||||
if content.size > settings.FARGO_MAX_DOCUMENT_SIZE:
|
||||
raise forms.ValidationError(_('Uploaded file is too big (limit is %s)') %
|
||||
filesizeformat(settings.FARGO_MAX_DOCUMENT_SIZE))
|
||||
raise forms.ValidationError(
|
||||
_('Uploaded file is too big (limit is %s)')
|
||||
% filesizeformat(settings.FARGO_MAX_DOCUMENT_SIZE)
|
||||
)
|
||||
return content
|
||||
|
||||
def clean(self):
|
||||
content = self.cleaned_data.get('content')
|
||||
if content:
|
||||
if (models.Document.objects.used_space(self.instance.user) + content.size
|
||||
> settings.FARGO_MAX_DOCUMENT_BOX_SIZE):
|
||||
raise forms.ValidationError(_('Your document box is full (limit is %s)') %
|
||||
settings.FARGO_MAX_DOCUMENT_BOX_SIZE)
|
||||
if (
|
||||
models.Document.objects.used_space(self.instance.user) + content.size
|
||||
> settings.FARGO_MAX_DOCUMENT_BOX_SIZE
|
||||
):
|
||||
raise forms.ValidationError(
|
||||
_('Your document box is full (limit is %s)') % settings.FARGO_MAX_DOCUMENT_BOX_SIZE
|
||||
)
|
||||
return self.cleaned_data
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.instance.filename = self.files['content'].name[:512]
|
||||
self.instance.document = models.Document.objects.get_by_file(
|
||||
self.files['content'])
|
||||
self.instance.document = models.Document.objects.get_by_file(self.files['content'])
|
||||
return super(UploadForm, self).save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
|
@ -59,6 +62,4 @@ class EditForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = models.UserDocument
|
||||
fields = ['title', 'description', 'expiration_date']
|
||||
widgets = {
|
||||
'expiration_date': forms.TextInput(attrs={'type': 'date'})
|
||||
}
|
||||
widgets = {'expiration_date': forms.TextInput(attrs={'type': 'date'})}
|
||||
|
|
|
@ -28,16 +28,15 @@ class DocumentManager(models.Manager):
|
|||
n = n or now()
|
||||
# use a window of 60 seconds to be sure this document will never be used
|
||||
qs = self.filter(creation_date__lt=n - datetime.timedelta(seconds=60))
|
||||
qs = qs.filter(user_documents__isnull=True,
|
||||
oauth2_tempfiles__isnull=True)
|
||||
qs = qs.filter(user_documents__isnull=True, oauth2_tempfiles__isnull=True)
|
||||
for document in qs:
|
||||
document.content.delete(False)
|
||||
qs.delete()
|
||||
|
||||
def get_by_file(self, f):
|
||||
'''Get document with the same SHA-256 hash as the passde Django file
|
||||
like object.
|
||||
'''
|
||||
"""Get document with the same SHA-256 hash as the passde Django file
|
||||
like object.
|
||||
"""
|
||||
file_hash = utils.sha256_of_file(f)
|
||||
try:
|
||||
o = self.get(content_hash=file_hash)
|
||||
|
|
|
@ -15,14 +15,21 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Document',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('document_filename', models.CharField(max_length=512, verbose_name='document filename')),
|
||||
('document_file', models.FileField(upload_to=b'', verbose_name='file')),
|
||||
('creation', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
|
||||
('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
},
|
||||
options={},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -12,7 +12,25 @@ import jsonfield.fields
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('fargo', '0001_initial'), ('fargo', '0002_auto_20150818_2117'), ('fargo', '0003_auto_20150924_1056'), ('fargo', '0004_auto_20160212_0936'), ('fargo', '0005_auto_20160312_1809'), ('fargo', '0006_fill_new_columns'), ('fargo', '0007_auto_20160312_1816'), ('fargo', '0008_validation_origin'), ('fargo', '0009_auto_20160326_2104'), ('fargo', '0010_auto_20160413_0809'), ('fargo', '0011_userdocument_deletable_by_user'), ('fargo', '0012_auto_20161124_0626'), ('fargo', '0013_document_mime_type'), ('fargo', '0014_auto_20171016_0854'), ('fargo', '0015_document_creation_date'), ('fargo', '0016_auto_20180330_2248'), ('fargo', '0017_auto_20180331_1532')]
|
||||
replaces = [
|
||||
('fargo', '0001_initial'),
|
||||
('fargo', '0002_auto_20150818_2117'),
|
||||
('fargo', '0003_auto_20150924_1056'),
|
||||
('fargo', '0004_auto_20160212_0936'),
|
||||
('fargo', '0005_auto_20160312_1809'),
|
||||
('fargo', '0006_fill_new_columns'),
|
||||
('fargo', '0007_auto_20160312_1816'),
|
||||
('fargo', '0008_validation_origin'),
|
||||
('fargo', '0009_auto_20160326_2104'),
|
||||
('fargo', '0010_auto_20160413_0809'),
|
||||
('fargo', '0011_userdocument_deletable_by_user'),
|
||||
('fargo', '0012_auto_20161124_0626'),
|
||||
('fargo', '0013_document_mime_type'),
|
||||
('fargo', '0014_auto_20171016_0854'),
|
||||
('fargo', '0015_document_creation_date'),
|
||||
('fargo', '0016_auto_20180330_2248'),
|
||||
('fargo', '0017_auto_20180331_1532'),
|
||||
]
|
||||
|
||||
initial = True
|
||||
|
||||
|
@ -24,7 +42,12 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Document',
|
||||
fields=[
|
||||
('content_hash', models.CharField(max_length=128, serialize=False, verbose_name='content hash', primary_key=True)),
|
||||
(
|
||||
'content_hash',
|
||||
models.CharField(
|
||||
max_length=128, serialize=False, verbose_name='content hash', primary_key=True
|
||||
),
|
||||
),
|
||||
('content', models.FileField(upload_to=b'uploads/', max_length=300, verbose_name='file')),
|
||||
('mime_type', models.CharField(max_length=256, blank=True)),
|
||||
('creation_date', models.DateTimeField(auto_now_add=True)),
|
||||
|
@ -38,7 +61,10 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Origin',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('label', models.CharField(max_length=80, verbose_name='Label')),
|
||||
('slug', models.SlugField(verbose_name='Slug')),
|
||||
],
|
||||
|
@ -46,16 +72,40 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='UserDocument',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('filename', models.CharField(max_length=512, verbose_name='filename')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
|
||||
('deletable_by_user', models.BooleanField(default=True, verbose_name='deletable by user')),
|
||||
('title', models.CharField(max_length=200, verbose_name='title', blank=True)),
|
||||
('description', models.TextField(verbose_name='description', blank=True)),
|
||||
('expiration_date', models.DateField(null=True, verbose_name='expiration date', blank=True)),
|
||||
('document', models.ForeignKey(related_name='user_documents', verbose_name='document', to='fargo.Document', on_delete=models.CASCADE)),
|
||||
('origin', models.ForeignKey(verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE)),
|
||||
('user', models.ForeignKey(related_name='user_documents', verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
(
|
||||
'document',
|
||||
models.ForeignKey(
|
||||
related_name='user_documents',
|
||||
verbose_name='document',
|
||||
to='fargo.Document',
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
'origin',
|
||||
models.ForeignKey(
|
||||
verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
related_name='user_documents',
|
||||
verbose_name='user',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-created', 'user'),
|
||||
|
@ -66,16 +116,32 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Validation',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('content_hash', models.CharField(max_length=128, null=True, verbose_name='content hash', blank=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
(
|
||||
'content_hash',
|
||||
models.CharField(max_length=128, null=True, verbose_name='content hash', blank=True),
|
||||
),
|
||||
('document_type', models.CharField(max_length=256, verbose_name='document type')),
|
||||
('data', jsonfield.fields.JSONField(null=True, verbose_name='data')),
|
||||
('start', models.DateField(verbose_name='start date')),
|
||||
('end', models.DateField(verbose_name='end date')),
|
||||
('creator', models.CharField(max_length=256, verbose_name='creator')),
|
||||
('created', models.DateTimeField(verbose_name='creation date')),
|
||||
('origin', models.ForeignKey(verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE)),
|
||||
('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
(
|
||||
'origin',
|
||||
models.ForeignKey(
|
||||
verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -13,6 +13,10 @@ class Migration(migrations.Migration):
|
|||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='document',
|
||||
options={'ordering': ('-creation',), 'verbose_name': 'document', 'verbose_name_plural': 'documents'},
|
||||
options={
|
||||
'ordering': ('-creation',),
|
||||
'verbose_name': 'document',
|
||||
'verbose_name_plural': 'documents',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -29,7 +29,10 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='UserDocument',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('filename', models.CharField(max_length=512, verbose_name='filename')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
|
||||
],
|
||||
|
@ -42,19 +45,31 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Validation',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('document_type', models.CharField(max_length=256, verbose_name='document type')),
|
||||
('data', jsonfield.fields.JSONField(null=True, verbose_name='data')),
|
||||
('start', models.DateField(verbose_name='start date')),
|
||||
('end', models.DateField(verbose_name='end date')),
|
||||
('creator', models.CharField(max_length=256, verbose_name='creator')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
|
||||
('user_document', models.ForeignKey(verbose_name='user document', to='fargo.UserDocument', on_delete=models.CASCADE)),
|
||||
(
|
||||
'user_document',
|
||||
models.ForeignKey(
|
||||
verbose_name='user document', to='fargo.UserDocument', on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='document',
|
||||
options={'ordering': ('content_hash',), 'verbose_name': 'document', 'verbose_name_plural': 'documents'},
|
||||
options={
|
||||
'ordering': ('content_hash',),
|
||||
'verbose_name': 'document',
|
||||
'verbose_name_plural': 'documents',
|
||||
},
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='document',
|
||||
|
@ -80,18 +95,31 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='document',
|
||||
name='content_hash',
|
||||
field=models.CharField(default=datetime.datetime(2015, 9, 24, 10, 56, 54, 873399, tzinfo=utc), max_length=128, serialize=False, verbose_name='content hash', primary_key=True),
|
||||
field=models.CharField(
|
||||
default=datetime.datetime(2015, 9, 24, 10, 56, 54, 873399, tzinfo=utc),
|
||||
max_length=128,
|
||||
serialize=False,
|
||||
verbose_name='content hash',
|
||||
primary_key=True,
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userdocument',
|
||||
name='document',
|
||||
field=models.ForeignKey(related_name='user_documents', verbose_name='document', to='fargo.Document', on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
related_name='user_documents',
|
||||
verbose_name='document',
|
||||
to='fargo.Document',
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userdocument',
|
||||
name='user',
|
||||
field=models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
migrations.RunPython(noop, clear_document),
|
||||
]
|
||||
|
|
|
@ -14,18 +14,22 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Origin',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('label', models.CharField(max_length=80, verbose_name='Label')),
|
||||
('slug', models.SlugField(verbose_name='Slug')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
options={},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='userdocument',
|
||||
name='origin',
|
||||
field=models.ForeignKey(verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE
|
||||
),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -21,6 +21,8 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='validation',
|
||||
name='user',
|
||||
field=models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user', to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -19,6 +19,8 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='validation',
|
||||
name='user',
|
||||
field=models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -14,6 +14,8 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='validation',
|
||||
name='origin',
|
||||
field=models.ForeignKey(verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
verbose_name='origin', to='fargo.Origin', null=True, on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -15,7 +15,12 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='userdocument',
|
||||
name='user',
|
||||
field=models.ForeignKey(related_name='user_documents', verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(
|
||||
related_name='user_documents',
|
||||
verbose_name='user',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='validation',
|
||||
|
|
|
@ -14,6 +14,10 @@ class Migration(migrations.Migration):
|
|||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='document',
|
||||
options={'ordering': ('creation_date',), 'verbose_name': 'document', 'verbose_name_plural': 'documents'},
|
||||
options={
|
||||
'ordering': ('creation_date',),
|
||||
'verbose_name': 'document',
|
||||
'verbose_name_plural': 'documents',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -59,41 +59,23 @@ class Origin(models.Model):
|
|||
@python_2_unicode_compatible
|
||||
class UserDocument(models.Model):
|
||||
'''Document uploaded by an user or an agent'''
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_('user'),
|
||||
related_name='user_documents',
|
||||
on_delete=models.CASCADE)
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
document = models.ForeignKey(
|
||||
'Document',
|
||||
related_name='user_documents',
|
||||
verbose_name=_('document'),
|
||||
on_delete=models.CASCADE)
|
||||
filename = models.CharField(
|
||||
verbose_name=_('filename'),
|
||||
max_length=512)
|
||||
created = models.DateTimeField(
|
||||
verbose_name=_('creation date'),
|
||||
auto_now_add=True)
|
||||
origin = models.ForeignKey(
|
||||
Origin,
|
||||
verbose_name=_('origin'),
|
||||
null=True,
|
||||
on_delete=models.CASCADE)
|
||||
deletable_by_user = models.BooleanField(
|
||||
verbose_name=_('deletable by user'),
|
||||
default=True)
|
||||
title = models.CharField(
|
||||
verbose_name=_('title'),
|
||||
max_length=200,
|
||||
blank=True)
|
||||
description = models.TextField(
|
||||
verbose_name=_('description'),
|
||||
blank=True)
|
||||
expiration_date = models.DateField(
|
||||
verbose_name=_('expiration date'),
|
||||
blank=True,
|
||||
null=True)
|
||||
'Document', related_name='user_documents', verbose_name=_('document'), on_delete=models.CASCADE
|
||||
)
|
||||
filename = models.CharField(verbose_name=_('filename'), max_length=512)
|
||||
created = models.DateTimeField(verbose_name=_('creation date'), auto_now_add=True)
|
||||
origin = models.ForeignKey(Origin, verbose_name=_('origin'), null=True, on_delete=models.CASCADE)
|
||||
deletable_by_user = models.BooleanField(verbose_name=_('deletable by user'), default=True)
|
||||
title = models.CharField(verbose_name=_('title'), max_length=200, blank=True)
|
||||
description = models.TextField(verbose_name=_('description'), blank=True)
|
||||
expiration_date = models.DateField(verbose_name=_('expiration date'), blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('user document')
|
||||
|
@ -125,28 +107,19 @@ class UserDocument(models.Model):
|
|||
return ''
|
||||
return 'mime-%s mime-%s' % (
|
||||
self.document.mime_type.split('/')[0],
|
||||
re.sub(r'[/\.+-]', '-', self.document.mime_type))
|
||||
re.sub(r'[/\.+-]', '-', self.document.mime_type),
|
||||
)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Validation(models.Model):
|
||||
'''Validation of a document as special kind for an user,
|
||||
the data field contains metadata extracted from the document.
|
||||
'''
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=_('user'),
|
||||
on_delete=models.CASCADE)
|
||||
content_hash = models.CharField(
|
||||
max_length=128,
|
||||
verbose_name=_('content hash'),
|
||||
blank=True,
|
||||
null=True)
|
||||
origin = models.ForeignKey(
|
||||
Origin,
|
||||
verbose_name=_('origin'),
|
||||
null=True,
|
||||
on_delete=models.CASCADE)
|
||||
"""Validation of a document as special kind for an user,
|
||||
the data field contains metadata extracted from the document.
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
|
||||
content_hash = models.CharField(max_length=128, verbose_name=_('content hash'), blank=True, null=True)
|
||||
origin = models.ForeignKey(Origin, verbose_name=_('origin'), null=True, on_delete=models.CASCADE)
|
||||
document_type = models.CharField(max_length=256, verbose_name=_('document type'))
|
||||
data = JSONField(null=True, verbose_name=_('data'))
|
||||
start = models.DateField(verbose_name=_('start date'))
|
||||
|
@ -172,11 +145,14 @@ class Validation(models.Model):
|
|||
parts = []
|
||||
for meta_field in self.metadata:
|
||||
parts.append(
|
||||
_(u'%(label)s: %(value)s') % {
|
||||
_(u'%(label)s: %(value)s')
|
||||
% {
|
||||
'label': meta_field['label'],
|
||||
'value': self.data.get(meta_field['varname'], ''),
|
||||
})
|
||||
}
|
||||
)
|
||||
return force_text(u'; '.join(parts))
|
||||
|
||||
display.short_description = _('description')
|
||||
|
||||
@property
|
||||
|
@ -193,17 +169,10 @@ class Validation(models.Model):
|
|||
|
||||
class Document(models.Model):
|
||||
'''Content indexed documents'''
|
||||
content_hash = models.CharField(
|
||||
primary_key=True,
|
||||
max_length=128,
|
||||
verbose_name=_('content hash'))
|
||||
content = models.FileField(
|
||||
upload_to='uploads/',
|
||||
max_length=300,
|
||||
verbose_name=_('file'))
|
||||
mime_type = models.CharField(
|
||||
max_length=256,
|
||||
blank=True)
|
||||
|
||||
content_hash = models.CharField(primary_key=True, max_length=128, verbose_name=_('content hash'))
|
||||
content = models.FileField(upload_to='uploads/', max_length=300, verbose_name=_('file'))
|
||||
mime_type = models.CharField(max_length=256, blank=True)
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
objects = managers.DocumentManager()
|
||||
|
@ -247,10 +216,12 @@ class Document(models.Model):
|
|||
if not thumbnail:
|
||||
return ''
|
||||
|
||||
return format_html('<img width="{}" height="{}" src="{}"/>',
|
||||
thumbnail.width,
|
||||
thumbnail.height,
|
||||
self.thumbnail_data_url)
|
||||
return format_html(
|
||||
'<img width="{}" height="{}" src="{}"/>',
|
||||
thumbnail.width,
|
||||
thumbnail.height,
|
||||
self.thumbnail_data_url,
|
||||
)
|
||||
|
||||
@property
|
||||
def thumbnail_image(self):
|
||||
|
|
|
@ -23,9 +23,12 @@ from . import models
|
|||
|
||||
class DocumentTable(tables.Table):
|
||||
'''Display the list of documents of the user'''
|
||||
size = tables.TemplateColumn(template_code='{{ record.document.content.size|filesizeformat }}',
|
||||
orderable=False,
|
||||
verbose_name=_('Size'))
|
||||
|
||||
size = tables.TemplateColumn(
|
||||
template_code='{{ record.document.content.size|filesizeformat }}',
|
||||
orderable=False,
|
||||
verbose_name=_('Size'),
|
||||
)
|
||||
created = tables.DateTimeColumn(verbose_name=_('Creation Date'))
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -24,8 +24,13 @@ from django.views.generic import CreateView, DeleteView, UpdateView, View, Templ
|
|||
from django.urls import reverse, reverse_lazy
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import get_object_or_404, resolve_url
|
||||
from django.http import (HttpResponse, HttpResponseRedirect,
|
||||
HttpResponseBadRequest, HttpResponseForbidden, Http404)
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseRedirect,
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseForbidden,
|
||||
Http404,
|
||||
)
|
||||
from django.core import signing
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import get_user_model, REDIRECT_FIELD_NAME
|
||||
|
@ -47,6 +52,7 @@ from . import models, forms, tables
|
|||
try:
|
||||
from mellon.utils import get_idps
|
||||
except ImportError:
|
||||
|
||||
def get_idps():
|
||||
return []
|
||||
|
||||
|
@ -58,9 +64,7 @@ class Logger(object):
|
|||
|
||||
class Documents(object):
|
||||
def get_queryset(self):
|
||||
return models.UserDocument.objects \
|
||||
.filter(user=self.request.user) \
|
||||
.select_related('document', 'user')
|
||||
return models.UserDocument.objects.filter(user=self.request.user).select_related('document', 'user')
|
||||
|
||||
@cached_property
|
||||
def count(self):
|
||||
|
@ -78,15 +82,14 @@ class CommonUpload(Logger, Documents, CreateView):
|
|||
|
||||
def get_form_kwargs(self, **kwargs):
|
||||
kwargs = super(CommonUpload, self).get_form_kwargs(**kwargs)
|
||||
kwargs['instance'] = models.UserDocument(
|
||||
user=self.request.user)
|
||||
kwargs['instance'] = models.UserDocument(user=self.request.user)
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
result = super(CommonUpload, self).form_valid(form)
|
||||
self.logger.info(u'user uploaded file %s (sha256=%s)',
|
||||
self.object.filename,
|
||||
self.object.document.content_hash)
|
||||
self.logger.info(
|
||||
u'user uploaded file %s (sha256=%s)', self.object.filename, self.object.document.content_hash
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
|
@ -108,6 +111,7 @@ class Upload(CommonUpload):
|
|||
|
||||
class Homepage(SingleTableMixin, CommonUpload):
|
||||
'''Show documents of users, eventually paginate and sort them.'''
|
||||
|
||||
template_name = 'fargo/home.html'
|
||||
form_class = forms.UploadForm
|
||||
table_class = tables.DocumentTable
|
||||
|
@ -134,7 +138,6 @@ class Homepage(SingleTableMixin, CommonUpload):
|
|||
return super(CommonUpload, self).post(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
class PickView(object):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.pick_url = request.GET.get('pick')
|
||||
|
@ -160,8 +163,7 @@ class Delete(Logger, Documents, DeleteView):
|
|||
raise PermissionDenied()
|
||||
result = super(Delete, self).delete(request, *args, **kwargs)
|
||||
messages.info(request, _('File %s deleted') % self.object.filename)
|
||||
self.logger.info('user deleted file %r(%s)', self.object.filename,
|
||||
self.object.pk)
|
||||
self.logger.info('user deleted file %r(%s)', self.object.filename, self.object.pk)
|
||||
return result
|
||||
|
||||
def get_success_url(self):
|
||||
|
@ -185,39 +187,41 @@ class Pick(PickView, Documents, Logger, View):
|
|||
http_method_allowed = ['post']
|
||||
|
||||
def post(self, request, pk):
|
||||
user_document = get_object_or_404(self.get_queryset(), pk=pk,
|
||||
user=request.user)
|
||||
user_document = get_object_or_404(self.get_queryset(), pk=pk, user=request.user)
|
||||
token = signing.dumps(user_document.pk)
|
||||
download_url = make_url(
|
||||
reverse('remote_download', kwargs={'filename': user_document.filename}),
|
||||
token=token,
|
||||
request=request)
|
||||
self.logger.info(u'user picked file %s sha256 %s returned to %s',
|
||||
user_document.filename,
|
||||
user_document.document.content_hash, pick)
|
||||
request=request,
|
||||
)
|
||||
self.logger.info(
|
||||
u'user picked file %s sha256 %s returned to %s',
|
||||
user_document.filename,
|
||||
user_document.document.content_hash,
|
||||
pick,
|
||||
)
|
||||
return HttpResponseRedirect(make_url(self.pick_url, url=download_url))
|
||||
|
||||
|
||||
class Download(Documents, Logger, View):
|
||||
def get(self, request, pk, filename):
|
||||
user_document = get_object_or_404(self.get_queryset(), pk=pk,
|
||||
user=self.request.user)
|
||||
self.logger.info('user download file %s with hash %s',
|
||||
user_document.filename,
|
||||
user_document.document.content_hash)
|
||||
user_document = get_object_or_404(self.get_queryset(), pk=pk, user=self.request.user)
|
||||
self.logger.info(
|
||||
'user download file %s with hash %s', user_document.filename, user_document.document.content_hash
|
||||
)
|
||||
return self.return_user_document(user_document)
|
||||
|
||||
def return_user_document(self, user_document):
|
||||
response = HttpResponse(user_document.document.content.chunks(),
|
||||
content_type='application/octet-stream')
|
||||
response = HttpResponse(
|
||||
user_document.document.content.chunks(), content_type='application/octet-stream'
|
||||
)
|
||||
response['Content-disposition'] = 'attachment'
|
||||
return response
|
||||
|
||||
|
||||
class Thumbnail(Documents, View):
|
||||
def get(self, request, pk, filename):
|
||||
user_document = get_object_or_404(self.get_queryset(), pk=pk,
|
||||
user=self.request.user)
|
||||
user_document = get_object_or_404(self.get_queryset(), pk=pk, user=self.request.user)
|
||||
thumbnail = user_document.document.thumbnail
|
||||
if not thumbnail:
|
||||
raise Http404
|
||||
|
@ -226,6 +230,7 @@ class Thumbnail(Documents, View):
|
|||
|
||||
class RemoteDownload(Download):
|
||||
'''Allow downloading any file given the URL contains a signed token'''
|
||||
|
||||
def get(self, request, filename):
|
||||
if 'token' not in request.GET:
|
||||
return HttpResponseForbidden('missing token')
|
||||
|
@ -240,11 +245,13 @@ class RemoteDownload(Download):
|
|||
except signing.BadSignature:
|
||||
return HttpResponseForbidden('token signature is invalid')
|
||||
user_document = get_object_or_404(models.UserDocument, pk=pk)
|
||||
self.logger.info('anonymous download of file %s from user %s(%s) with hash %s',
|
||||
user_document.filename,
|
||||
user_document.user,
|
||||
user_document.user.pk,
|
||||
user_document.document.content_hash)
|
||||
self.logger.info(
|
||||
'anonymous download of file %s from user %s(%s) with hash %s',
|
||||
user_document.filename,
|
||||
user_document.user,
|
||||
user_document.user.pk,
|
||||
user_document.document.content_hash,
|
||||
)
|
||||
return self.return_user_document(user_document)
|
||||
|
||||
|
||||
|
@ -252,20 +259,19 @@ class JSONP(Documents, View):
|
|||
def get_data(self, request):
|
||||
d = []
|
||||
for user_document in self.get_queryset():
|
||||
url = reverse('download',
|
||||
kwargs={'pk': user_document.pk,
|
||||
'filename': user_document.filename})
|
||||
url = reverse('download', kwargs={'pk': user_document.pk, 'filename': user_document.filename})
|
||||
url = request.build_absolute_uri(url)
|
||||
d.append({
|
||||
'filename': user_document.filename,
|
||||
'url': url,
|
||||
})
|
||||
d.append(
|
||||
{
|
||||
'filename': user_document.filename,
|
||||
'url': url,
|
||||
}
|
||||
)
|
||||
return d
|
||||
|
||||
def get(self, request):
|
||||
callback = request.GET.get('callback', 'callback')
|
||||
s = '%s(%s)' % (callback.encode('ascii'),
|
||||
dumps(self.get_data(request)))
|
||||
s = '%s(%s)' % (callback.encode('ascii'), dumps(self.get_data(request)))
|
||||
return HttpResponse(s, content_type='application/javascript')
|
||||
|
||||
|
||||
|
@ -277,8 +283,7 @@ class JSON(JSONP):
|
|||
request.user = get_object_or_404(User, username=username)
|
||||
elif not request.user.is_authenticated():
|
||||
return method_decorator(login_required)(JSON.get)(self, request)
|
||||
response = HttpResponse(dumps(self.get_data(request)),
|
||||
content_type='application/json')
|
||||
response = HttpResponse(dumps(self.get_data(request)), content_type='application/json')
|
||||
response['Access-Control-Allow-Origin'] = '*'
|
||||
return response
|
||||
|
||||
|
|
|
@ -26,17 +26,28 @@ class OAuth2ClientAdmin(admin.ModelAdmin):
|
|||
|
||||
|
||||
class OAuth2AuthorizeAdmin(admin.ModelAdmin):
|
||||
list_display = ['id', 'client_name', 'user_document', 'thumbnail',
|
||||
'access_token', 'code', 'creation_date']
|
||||
list_display = [
|
||||
'id',
|
||||
'client_name',
|
||||
'user_document',
|
||||
'thumbnail',
|
||||
'access_token',
|
||||
'code',
|
||||
'creation_date',
|
||||
]
|
||||
raw_id_fields = ['user_document']
|
||||
search_fields = ['client__client_name', 'user_document__user__email',
|
||||
'user_document__user__first_name',
|
||||
'user_document__user__last_name',
|
||||
'user_document__filename',
|
||||
'user_document__user__contenat_has']
|
||||
search_fields = [
|
||||
'client__client_name',
|
||||
'user_document__user__email',
|
||||
'user_document__user__first_name',
|
||||
'user_document__user__last_name',
|
||||
'user_document__filename',
|
||||
'user_document__user__contenat_has',
|
||||
]
|
||||
|
||||
def thumbnail(self, instance):
|
||||
return instance.user_document.document.thumbnail_img_tag
|
||||
|
||||
thumbnail.short_description = _('thumbnail')
|
||||
|
||||
def client_name(self, instance):
|
||||
|
@ -48,9 +59,9 @@ class OAuth2TempFileAdmin(admin.ModelAdmin):
|
|||
raw_id_fields = ['document']
|
||||
search_fields = ['filename', 'uuid', 'client__client_name']
|
||||
|
||||
|
||||
def thumbnail(self, instance):
|
||||
return instance.document.thumbnail_img_tag
|
||||
|
||||
thumbnail.short_description = _('thumbnail')
|
||||
|
||||
def client_name(self, instance):
|
||||
|
|
|
@ -30,8 +30,7 @@ from .models import OAuth2Client
|
|||
|
||||
|
||||
class OAuth2User(object):
|
||||
""" Fake user class to return in case OAuth2 Client authentication
|
||||
"""
|
||||
"""Fake user class to return in case OAuth2 Client authentication"""
|
||||
|
||||
def __init__(self, oauth2_client):
|
||||
self.oauth2_client = oauth2_client
|
||||
|
@ -57,11 +56,10 @@ class OAuth2User(object):
|
|||
|
||||
|
||||
class FargoOAUTH2Authentication(BasicAuthentication):
|
||||
|
||||
def authenticate_through_idp(self, client_id, client_secret):
|
||||
'''Check client_id and client_secret with configured IdP, and verify it is an OIDC
|
||||
client.
|
||||
'''
|
||||
"""Check client_id and client_secret with configured IdP, and verify it is an OIDC
|
||||
client.
|
||||
"""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
authentic_idp = getattr(settings, 'FARGO_IDP_URL', None)
|
||||
|
@ -71,9 +69,12 @@ class FargoOAUTH2Authentication(BasicAuthentication):
|
|||
|
||||
url = urlparse.urljoin(authentic_idp, 'api/check-password/')
|
||||
try:
|
||||
response = requests.post(url, json={
|
||||
'username': client_id,
|
||||
'password': client_secret}, auth=(client_id, client_secret), verify=False)
|
||||
response = requests.post(
|
||||
url,
|
||||
json={'username': client_id, 'password': client_secret},
|
||||
auth=(client_id, client_secret),
|
||||
verify=False,
|
||||
)
|
||||
response.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
logger.warning(u'idp check-password API failed: %s', e)
|
||||
|
@ -92,8 +93,7 @@ class FargoOAUTH2Authentication(BasicAuthentication):
|
|||
|
||||
def authenticate_credentials(self, client_id, client_secret, request=None):
|
||||
try:
|
||||
client = OAuth2Client.objects.get(
|
||||
client_id=client_id, client_secret=client_secret)
|
||||
client = OAuth2Client.objects.get(client_id=client_id, client_secret=client_secret)
|
||||
except OAuth2Client.DoesNotExist:
|
||||
success, error = self.authenticate_through_idp(client_id, client_secret)
|
||||
if not success:
|
||||
|
|
|
@ -41,8 +41,7 @@ class Command(BaseCommand):
|
|||
f = ContentFile(file_object.read(), name=filename)
|
||||
document = Document.objects.get_by_file(f)
|
||||
oauth2_document = OAuth2TempFile.objects.create(
|
||||
client=client,
|
||||
document=document,
|
||||
filename=filename)
|
||||
client=client, document=document, filename=filename
|
||||
)
|
||||
uri = reverse('oauth2-put-document-authorize', args=[oauth2_document.pk])
|
||||
self.stdout.write('https://localhost:8000' + make_url(uri, redirect_uri=redirect_uri))
|
||||
|
|
|
@ -15,7 +15,10 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='OAuth2Authorize',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('access_token', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
('code', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
('creation_date', models.DateTimeField(auto_now=True)),
|
||||
|
@ -25,11 +28,22 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='OAuth2Client',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('client_secret', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
(
|
||||
'client_secret',
|
||||
models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255),
|
||||
),
|
||||
('client_id', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
('client_name', models.CharField(max_length=255)),
|
||||
('redirect_uris', models.TextField(verbose_name='redirect URIs', validators=[fargo.oauth2.models.validate_https_url])),
|
||||
(
|
||||
'redirect_uris',
|
||||
models.TextField(
|
||||
verbose_name='redirect URIs', validators=[fargo.oauth2.models.validate_https_url]
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
|
@ -37,7 +51,12 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('hash_key', models.CharField(max_length=128, serialize=False, primary_key=True)),
|
||||
('filename', models.CharField(max_length=512)),
|
||||
('document', models.ForeignKey(to='fargo.Document', related_name='oauth2_tempfiles', on_delete=models.CASCADE)),
|
||||
(
|
||||
'document',
|
||||
models.ForeignKey(
|
||||
to='fargo.Document', related_name='oauth2_tempfiles', on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -9,7 +9,13 @@ import fargo.oauth2.models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('oauth2', '0001_initial'), ('oauth2', '0002_auto_20180321_2343'), ('oauth2', '0003_auto_20180322_1016'), ('oauth2', '0004_auto_20180326_1330'), ('oauth2', '0005_auto_20180331_1532')]
|
||||
replaces = [
|
||||
('oauth2', '0001_initial'),
|
||||
('oauth2', '0002_auto_20180321_2343'),
|
||||
('oauth2', '0003_auto_20180322_1016'),
|
||||
('oauth2', '0004_auto_20180326_1330'),
|
||||
('oauth2', '0005_auto_20180331_1532'),
|
||||
]
|
||||
|
||||
initial = True
|
||||
|
||||
|
@ -21,7 +27,10 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='OAuth2Authorize',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('access_token', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
('code', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
('creation_date', models.DateTimeField(auto_now_add=True)),
|
||||
|
@ -35,11 +44,22 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='OAuth2Client',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('client_name', models.CharField(max_length=255)),
|
||||
('redirect_uris', models.TextField(verbose_name='redirect URIs', validators=[fargo.oauth2.models.validate_https_url])),
|
||||
(
|
||||
'redirect_uris',
|
||||
models.TextField(
|
||||
verbose_name='redirect URIs', validators=[fargo.oauth2.models.validate_https_url]
|
||||
),
|
||||
),
|
||||
('client_id', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
('client_secret', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)),
|
||||
(
|
||||
'client_secret',
|
||||
models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('client_name',),
|
||||
|
@ -50,11 +70,24 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='OAuth2TempFile',
|
||||
fields=[
|
||||
('uuid', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=32, serialize=False, primary_key=True)),
|
||||
(
|
||||
'uuid',
|
||||
models.CharField(
|
||||
default=fargo.oauth2.models.generate_uuid,
|
||||
max_length=32,
|
||||
serialize=False,
|
||||
primary_key=True,
|
||||
),
|
||||
),
|
||||
('filename', models.CharField(max_length=512)),
|
||||
('creation_date', models.DateTimeField(auto_now_add=True)),
|
||||
('client', models.ForeignKey(to='oauth2.OAuth2Client', on_delete=models.CASCADE)),
|
||||
('document', models.ForeignKey(related_name='oauth2_tempfiles', to='fargo.Document', on_delete=models.CASCADE)),
|
||||
(
|
||||
'document',
|
||||
models.ForeignKey(
|
||||
related_name='oauth2_tempfiles', to='fargo.Document', on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('creation_date',),
|
||||
|
|
|
@ -28,13 +28,17 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='oauth2authorize',
|
||||
name='client',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='oauth2.OAuth2Client'),
|
||||
field=models.ForeignKey(
|
||||
default=1, on_delete=django.db.models.deletion.CASCADE, to='oauth2.OAuth2Client'
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oauth2tempfile',
|
||||
name='client',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='oauth2.OAuth2Client'),
|
||||
field=models.ForeignKey(
|
||||
default=1, on_delete=django.db.models.deletion.CASCADE, to='oauth2.OAuth2Client'
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -25,6 +25,8 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='oauth2tempfile',
|
||||
name='uuid',
|
||||
field=models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=32, primary_key=True, serialize=False),
|
||||
field=models.CharField(
|
||||
default=fargo.oauth2.models.generate_uuid, max_length=32, primary_key=True, serialize=False
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -14,14 +14,26 @@ class Migration(migrations.Migration):
|
|||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='oauth2authorize',
|
||||
options={'ordering': ('creation_date',), 'verbose_name': 'OAUTH2 authorization', 'verbose_name_plural': 'OAUTH2 authorizations'},
|
||||
options={
|
||||
'ordering': ('creation_date',),
|
||||
'verbose_name': 'OAUTH2 authorization',
|
||||
'verbose_name_plural': 'OAUTH2 authorizations',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='oauth2client',
|
||||
options={'ordering': ('client_name',), 'verbose_name': 'OAUTH2 client', 'verbose_name_plural': 'OAUTH2 clients'},
|
||||
options={
|
||||
'ordering': ('client_name',),
|
||||
'verbose_name': 'OAUTH2 client',
|
||||
'verbose_name_plural': 'OAUTH2 clients',
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='oauth2tempfile',
|
||||
options={'ordering': ('creation_date',), 'verbose_name': 'OAUTH2 temporary file', 'verbose_name_plural': 'OAUTH2 temporary files'},
|
||||
options={
|
||||
'ordering': ('creation_date',),
|
||||
'verbose_name': 'OAUTH2 temporary file',
|
||||
'verbose_name_plural': 'OAUTH2 temporary files',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -50,9 +50,7 @@ def validate_https_url(data):
|
|||
@python_2_unicode_compatible
|
||||
class OAuth2Client(models.Model):
|
||||
client_name = models.CharField(max_length=255)
|
||||
redirect_uris = models.TextField(
|
||||
verbose_name=_('redirect URIs'),
|
||||
validators=[validate_https_url])
|
||||
redirect_uris = models.TextField(verbose_name=_('redirect URIs'), validators=[validate_https_url])
|
||||
client_id = models.CharField(max_length=255, default=generate_uuid)
|
||||
client_secret = models.CharField(max_length=255, default=generate_uuid)
|
||||
|
||||
|
@ -97,9 +95,7 @@ class OAuth2Authorize(models.Model):
|
|||
|
||||
@classmethod
|
||||
def get_lifetime(cls):
|
||||
return max(
|
||||
settings.FARGO_CODE_LIFETIME,
|
||||
settings.FARGO_ACCESS_TOKEN_LIFETIME)
|
||||
return max(settings.FARGO_CODE_LIFETIME, settings.FARGO_ACCESS_TOKEN_LIFETIME)
|
||||
|
||||
def __repr__(self):
|
||||
return 'OAuth2Authorize for document %r' % self.user_document
|
||||
|
|
|
@ -16,8 +16,14 @@
|
|||
|
||||
from django.conf.urls import url
|
||||
|
||||
from .views import (authorize_get_document, get_document_token, get_document,
|
||||
authorize_put_document, put_document, download_put_document)
|
||||
from .views import (
|
||||
authorize_get_document,
|
||||
get_document_token,
|
||||
get_document,
|
||||
authorize_put_document,
|
||||
put_document,
|
||||
download_put_document,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'get-document/authorize', authorize_get_document, name='oauth2-authorize'),
|
||||
|
|
|
@ -22,8 +22,7 @@ from django.utils.translation import ugettext as _
|
|||
from django.utils.timezone import now
|
||||
from django.core.files.base import ContentFile
|
||||
from django.urls import reverse
|
||||
from django.http import (HttpResponse, HttpResponseBadRequest,
|
||||
HttpResponseRedirect)
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import FormView, TemplateView, View
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
@ -99,12 +98,14 @@ class OAuth2AuthorizeView(FormView):
|
|||
def form_valid(self, form):
|
||||
document = form.cleaned_data['document']
|
||||
authorization = OAuth2Authorize.objects.create(client=self.client, user_document=document)
|
||||
logger.info(u'user %s authorized client "%s" to get document "%s" (%s) with code "%s"',
|
||||
self.request.user,
|
||||
self.client,
|
||||
document,
|
||||
document.pk,
|
||||
authorization.code)
|
||||
logger.info(
|
||||
u'user %s authorized client "%s" to get document "%s" (%s) with code "%s"',
|
||||
self.request.user,
|
||||
self.client,
|
||||
document,
|
||||
document.pk,
|
||||
authorization.code,
|
||||
)
|
||||
return self.redirect(code=authorization.code, state=self.state)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -135,28 +136,32 @@ class GetDocumentTokenView(OAUTH2APIViewMixin):
|
|||
|
||||
if (now() - authorize.creation_date).total_seconds() > settings.FARGO_CODE_LIFETIME:
|
||||
return self.error('invalid_grant', 'code is expired')
|
||||
logger.info(u'client "%s" resolved code "%s" to access token "%s"',
|
||||
request.user.oauth2_client,
|
||||
authorize.code,
|
||||
authorize.access_token)
|
||||
return Response({
|
||||
'access_token': authorize.access_token,
|
||||
'expires': settings.FARGO_ACCESS_TOKEN_LIFETIME
|
||||
})
|
||||
logger.info(
|
||||
u'client "%s" resolved code "%s" to access token "%s"',
|
||||
request.user.oauth2_client,
|
||||
authorize.code,
|
||||
authorize.access_token,
|
||||
)
|
||||
return Response(
|
||||
{'access_token': authorize.access_token, 'expires': settings.FARGO_ACCESS_TOKEN_LIFETIME}
|
||||
)
|
||||
|
||||
|
||||
get_document_token = GetDocumentTokenView.as_view()
|
||||
|
||||
|
||||
def document_response(user_document):
|
||||
response = HttpResponse(content=user_document.document.content.chunks(), status=200,
|
||||
content_type='application/octet-stream')
|
||||
response = HttpResponse(
|
||||
content=user_document.document.content.chunks(), status=200, content_type='application/octet-stream'
|
||||
)
|
||||
|
||||
filename = user_document.filename
|
||||
ascii_filename = filename.encode('ascii', 'replace').decode()
|
||||
percent_encoded_filename = quote(filename.encode('utf8'), safe='')
|
||||
response['Content-Disposition'] = 'attachment; filename="%s"; filename*=UTF-8\'\'%s' % (ascii_filename,
|
||||
percent_encoded_filename)
|
||||
response['Content-Disposition'] = 'attachment; filename="%s"; filename*=UTF-8\'\'%s' % (
|
||||
ascii_filename,
|
||||
percent_encoded_filename,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
|
@ -166,16 +171,17 @@ def get_document(request):
|
|||
return HttpResponseBadRequest('http bearer authentication failed: invalid authorization header')
|
||||
|
||||
user_document = oauth_authorize.user_document
|
||||
logger.info(u'client "%s" retrieved document "%s" (%s) with access token "%s"',
|
||||
oauth_authorize.client,
|
||||
user_document,
|
||||
user_document.pk,
|
||||
oauth_authorize.access_token)
|
||||
logger.info(
|
||||
u'client "%s" retrieved document "%s" (%s) with access token "%s"',
|
||||
oauth_authorize.client,
|
||||
user_document,
|
||||
user_document.pk,
|
||||
oauth_authorize.access_token,
|
||||
)
|
||||
return document_response(user_document)
|
||||
|
||||
|
||||
class PutDocumentAPIView(OAUTH2APIViewMixin):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
filename, error = get_content_disposition_value(request)
|
||||
if error:
|
||||
|
@ -184,17 +190,18 @@ class PutDocumentAPIView(OAUTH2APIViewMixin):
|
|||
f = ContentFile(request.body, name=filename)
|
||||
document = Document.objects.get_by_file(f)
|
||||
oauth2_document = OAuth2TempFile.objects.create(
|
||||
client=request.user.oauth2_client,
|
||||
document=document,
|
||||
filename=filename)
|
||||
client=request.user.oauth2_client, document=document, filename=filename
|
||||
)
|
||||
uri = reverse('oauth2-put-document-authorize', args=[oauth2_document.pk])
|
||||
|
||||
response = Response()
|
||||
response['Location'] = uri
|
||||
logger.info(u'client "%s" uploaded document "%s" (%s)',
|
||||
request.user.oauth2_client,
|
||||
filename,
|
||||
oauth2_document.pk)
|
||||
logger.info(
|
||||
u'client "%s" uploaded document "%s" (%s)',
|
||||
request.user.oauth2_client,
|
||||
filename,
|
||||
oauth2_document.pk,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
|
@ -221,11 +228,13 @@ class OAuth2AuthorizePutView(TemplateView):
|
|||
kwargs['filename'] = self.oauth2_document.filename
|
||||
kwargs['thumbnail_image'] = self.oauth2_document.document.thumbnail_image
|
||||
kwargs['oauth2_client'] = self.oauth2_document.client
|
||||
kwargs['download_url'] = reverse('oauth2-put-document-download', kwargs={'pk': self.oauth2_document.pk})
|
||||
kwargs['download_url'] = reverse(
|
||||
'oauth2-put-document-download', kwargs={'pk': self.oauth2_document.pk}
|
||||
)
|
||||
# verify if document already exists
|
||||
if not UserDocument.objects.filter(
|
||||
user=self.request.user,
|
||||
document=self.oauth2_document.document).exists():
|
||||
user=self.request.user, document=self.oauth2_document.document
|
||||
).exists():
|
||||
kwargs['error_message'] = ''
|
||||
else:
|
||||
kwargs['error_message'] = _('This document is already in your portfolio')
|
||||
|
@ -246,16 +255,20 @@ class OAuth2AuthorizePutView(TemplateView):
|
|||
UserDocument.objects.create(
|
||||
user=request.user,
|
||||
document=self.oauth2_document.document,
|
||||
filename=self.oauth2_document.filename)
|
||||
logger.info(u'user %s accepted document "%s" (%s) from client "%s"',
|
||||
request.user,
|
||||
self.oauth2_document.filename,
|
||||
self.oauth2_document.pk,
|
||||
self.oauth2_document.client)
|
||||
filename=self.oauth2_document.filename,
|
||||
)
|
||||
logger.info(
|
||||
u'user %s accepted document "%s" (%s) from client "%s"',
|
||||
request.user,
|
||||
self.oauth2_document.filename,
|
||||
self.oauth2_document.pk,
|
||||
self.oauth2_document.client,
|
||||
)
|
||||
return self.redirect()
|
||||
finally:
|
||||
self.oauth2_document.delete()
|
||||
|
||||
|
||||
authorize_put_document = login_required(OAuth2AuthorizePutView.as_view())
|
||||
|
||||
|
||||
|
@ -264,4 +277,5 @@ class DownloadPutDocument(View):
|
|||
oauth2_document = get_object_or_404(OAuth2TempFile, pk=kwargs['pk'])
|
||||
return document_response(oauth2_document)
|
||||
|
||||
|
||||
download_put_document = login_required(DownloadPutDocument.as_view())
|
||||
|
|
|
@ -28,6 +28,7 @@ from django.conf.global_settings import STATICFILES_FINDERS
|
|||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
|
@ -143,6 +144,7 @@ LOGOUT_URL = '/logout/'
|
|||
# Authentication settings
|
||||
try:
|
||||
import mellon
|
||||
|
||||
INSTALLED_APPS = INSTALLED_APPS + ('mellon',)
|
||||
except ImportError:
|
||||
mellon = None
|
||||
|
@ -182,11 +184,7 @@ FARGO_DOCUMENT_TYPES = [
|
|||
'name': 'avis-d-imposition',
|
||||
'label': u'Avis d\'imposition',
|
||||
'metadata': [
|
||||
{
|
||||
'label': u'Personne-s concernée-s',
|
||||
'varname': 'personnes_concernees',
|
||||
'type': 'string'
|
||||
},
|
||||
{'label': u'Personne-s concernée-s', 'varname': 'personnes_concernees', 'type': 'string'},
|
||||
{
|
||||
'label': u'Année',
|
||||
'varname': 'annee',
|
||||
|
@ -197,7 +195,7 @@ FARGO_DOCUMENT_TYPES = [
|
|||
'label': u'Revenu fiscal de référence',
|
||||
'varname': 'revenu_fiscal_de_reference',
|
||||
'type': 'string',
|
||||
'validation': ' *[0-9]+ *'
|
||||
'validation': ' *[0-9]+ *',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -227,9 +225,7 @@ LOGGING = {
|
|||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'formatters': {
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(asctime)s %(name)s: %(message)s'
|
||||
},
|
||||
'simple': {'format': '%(levelname)s %(asctime)s %(name)s: %(message)s'},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
|
@ -262,10 +258,9 @@ FARGO_CODE_LIFETIME = 300
|
|||
FARGO_ACCESS_TOKEN_LIFETIME = 3600
|
||||
FARGO_OAUTH2_TEMPFILE_LIFETIME = 86400
|
||||
|
||||
local_settings_file = os.environ.get('FARGO_SETTINGS_FILE',
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'local_settings.py'))
|
||||
local_settings_file = os.environ.get(
|
||||
'FARGO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')
|
||||
)
|
||||
|
||||
if os.path.exists(local_settings_file):
|
||||
exec(open(local_settings_file).read())
|
||||
|
|
|
@ -18,9 +18,23 @@ from django.conf import settings
|
|||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
|
||||
from .fargo.views import (home, jsonp, json, download, pick, delete, upload, edit,
|
||||
remote_download, login, logout, pick_list, document_types, thumbnail)
|
||||
from .fargo.api_views import (push_document, recent_documents, router)
|
||||
from .fargo.views import (
|
||||
home,
|
||||
jsonp,
|
||||
json,
|
||||
download,
|
||||
pick,
|
||||
delete,
|
||||
upload,
|
||||
edit,
|
||||
remote_download,
|
||||
login,
|
||||
logout,
|
||||
pick_list,
|
||||
document_types,
|
||||
thumbnail,
|
||||
)
|
||||
from .fargo.api_views import push_document, recent_documents, router
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', home, name='home'),
|
||||
|
@ -30,18 +44,14 @@ urlpatterns = [
|
|||
url(r'^(?P<pk>\d+)/edit/$', edit, name='edit'),
|
||||
url(r'^(?P<pk>\d+)/delete/$', delete, name='delete'),
|
||||
url(r'^(?P<pk>\d+)/pick/$', pick, name='pick'),
|
||||
url(r'^(?P<pk>\d+)/download/(?P<filename>[^/]*)$', download,
|
||||
name='download'),
|
||||
url(r'^(?P<pk>\d+)/thumbnail/(?P<filename>[^/]*)$', thumbnail,
|
||||
name='thumbnail'),
|
||||
url(r'^(?P<pk>\d+)/download/(?P<filename>[^/]*)$', download, name='download'),
|
||||
url(r'^(?P<pk>\d+)/thumbnail/(?P<filename>[^/]*)$', thumbnail, name='thumbnail'),
|
||||
url(r'^upload/$', upload, name='upload'),
|
||||
url(r'^remote-download/(?P<filename>[^/]*)$', remote_download,
|
||||
name='remote_download'),
|
||||
url(r'^remote-download/(?P<filename>[^/]*)$', remote_download, name='remote_download'),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^login/$', login, name='auth_login'),
|
||||
url(r'^logout/$', logout, name='auth_logout'),
|
||||
url(r'^document-types/$', document_types, name='document_types'),
|
||||
|
||||
url(r'^api/documents/push/$', push_document, name='fargo-api-push-document'),
|
||||
url(r'^api/documents/recently-added/$', recent_documents),
|
||||
url(r'^api/', include(router.urls)),
|
||||
|
@ -50,6 +60,7 @@ urlpatterns = [
|
|||
|
||||
if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS:
|
||||
import debug_toolbar
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^__debug__/', include(debug_toolbar.urls)),
|
||||
] + urlpatterns
|
||||
|
|
22
setup.py
22
setup.py
|
@ -26,28 +26,29 @@ class eo_sdist(sdist):
|
|||
|
||||
|
||||
def get_version():
|
||||
'''Use the VERSION, if absent generates a version with git describe, if not
|
||||
tag exists, take 0.0- and add the length of the commit log.
|
||||
'''
|
||||
"""Use the VERSION, if absent generates a version with git describe, if not
|
||||
tag exists, take 0.0- and add the length of the commit log.
|
||||
"""
|
||||
if os.path.exists('VERSION'):
|
||||
with open('VERSION', 'r') as v:
|
||||
return v.read()
|
||||
if os.path.exists('.git'):
|
||||
p = subprocess.Popen(['git','describe','--dirty=.dirty','--match=v*'],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
['git', 'describe', '--dirty=.dirty', '--match=v*'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
result = p.communicate()[0]
|
||||
if p.returncode == 0:
|
||||
result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v
|
||||
if '-' in result: # not a tagged version
|
||||
result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v
|
||||
if '-' in result: # not a tagged version
|
||||
real_number, commit_count, commit_hash = result.split('-', 2)
|
||||
version = '%s.post%s+%s' % (real_number, commit_count, commit_hash)
|
||||
else:
|
||||
version = result
|
||||
return version
|
||||
else:
|
||||
return '0.0.post%s' % len(
|
||||
subprocess.check_output(
|
||||
['git', 'rev-list', 'HEAD']).splitlines())
|
||||
return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines())
|
||||
return '0.0'
|
||||
|
||||
|
||||
|
@ -64,6 +65,7 @@ class compile_translations(Command):
|
|||
def run(self):
|
||||
try:
|
||||
from django.core.management import call_command
|
||||
|
||||
for path, dirs, files in os.walk('fargo'):
|
||||
if 'locale' not in dirs:
|
||||
continue
|
||||
|
|
|
@ -40,9 +40,9 @@ def app(request):
|
|||
|
||||
@pytest.fixture
|
||||
def concurrency(settings):
|
||||
'''Select a level of concurrency based on the db, sqlite3 is less robust
|
||||
thant postgres due to its transaction lock timeout of 5 seconds.
|
||||
'''
|
||||
"""Select a level of concurrency based on the db, sqlite3 is less robust
|
||||
thant postgres due to its transaction lock timeout of 5 seconds.
|
||||
"""
|
||||
if 'sqlite' in settings.DATABASES['default']['ENGINE']:
|
||||
return 20
|
||||
else:
|
||||
|
@ -53,11 +53,13 @@ def concurrency(settings):
|
|||
def private_settings(request):
|
||||
import django.conf
|
||||
from django.conf import UserSettingsHolder
|
||||
|
||||
old = django.conf.settings._wrapped
|
||||
django.conf.settings._wrapped = UserSettingsHolder(old)
|
||||
|
||||
def finalizer():
|
||||
django.conf.settings._wrapped = old
|
||||
|
||||
request.addfinalizer(finalizer)
|
||||
return django.conf.settings
|
||||
|
||||
|
@ -65,6 +67,7 @@ def private_settings(request):
|
|||
@pytest.fixture
|
||||
def caplog(caplog):
|
||||
import py.io
|
||||
|
||||
caplog.setLevel(logging.INFO)
|
||||
caplog.handler.stream = py.io.TextIO()
|
||||
caplog.handler.records = []
|
||||
|
@ -73,11 +76,7 @@ def caplog(caplog):
|
|||
|
||||
@pytest.fixture
|
||||
def john_doe(db):
|
||||
user = User(
|
||||
username='john.doe',
|
||||
first_name='John',
|
||||
last_name='Doe',
|
||||
email='john.doe@example.com')
|
||||
user = User(username='john.doe', first_name='John', last_name='Doe', email='john.doe@example.com')
|
||||
user.set_password('john.doe')
|
||||
user.save()
|
||||
return user
|
||||
|
@ -85,11 +84,7 @@ def john_doe(db):
|
|||
|
||||
@pytest.fixture
|
||||
def jane_doe(db):
|
||||
user = User(
|
||||
username='jane.doe',
|
||||
first_name='Jane',
|
||||
last_name='Doe',
|
||||
email='jane.doe@example.com')
|
||||
user = User(username='jane.doe', first_name='Jane', last_name='Doe', email='jane.doe@example.com')
|
||||
user.set_password('jane.doe')
|
||||
user.save()
|
||||
return user
|
||||
|
|
|
@ -34,16 +34,19 @@ def test_create_validation(settings, app, admin_user, john_doe, jane_doe):
|
|||
assert models.Validation.objects.count() == 0
|
||||
response = app.post_json(url, params=data, status=400)
|
||||
assert response.json['result'] == 0
|
||||
assert set(response.json['errors'].keys()) == set([field['varname'] for field in
|
||||
schema['metadata']] + ['creator', 'origin'])
|
||||
assert set(response.json['errors'].keys()) == set(
|
||||
[field['varname'] for field in schema['metadata']] + ['creator', 'origin']
|
||||
)
|
||||
assert models.Validation.objects.count() == 0
|
||||
data.update({
|
||||
'personnes_concernees': 'John and Lisa Doe',
|
||||
'annee': '2016',
|
||||
'revenu_fiscal_de_reference': '32455',
|
||||
'creator': 'FooBar',
|
||||
'origin': 'wcs.example.com',
|
||||
})
|
||||
data.update(
|
||||
{
|
||||
'personnes_concernees': 'John and Lisa Doe',
|
||||
'annee': '2016',
|
||||
'revenu_fiscal_de_reference': '32455',
|
||||
'creator': 'FooBar',
|
||||
'origin': 'wcs.example.com',
|
||||
}
|
||||
)
|
||||
response1 = app.post_json(url, params=data, status=201)
|
||||
assert set(response1.json.keys()) == set(['result', 'data'])
|
||||
assert response1.json['result'] == 1
|
||||
|
@ -76,13 +79,14 @@ def test_push_document(app, admin_user, john_doe):
|
|||
assert models.UserDocument.objects.count() == 0
|
||||
assert models.Document.objects.count() == 0
|
||||
assert response.json['result'] == 0
|
||||
assert (set(response.json['errors'].keys())
|
||||
== set(['origin', 'file_b64_content']))
|
||||
data.update({
|
||||
'origin': 'wcs',
|
||||
'file_b64_content': base64.b64encode(b'coin').decode(),
|
||||
'file_name': 'monfichier.pdf',
|
||||
})
|
||||
assert set(response.json['errors'].keys()) == set(['origin', 'file_b64_content'])
|
||||
data.update(
|
||||
{
|
||||
'origin': 'wcs',
|
||||
'file_b64_content': base64.b64encode(b'coin').decode(),
|
||||
'file_name': 'monfichier.pdf',
|
||||
}
|
||||
)
|
||||
response = app.post_json(url, params=data, status=200)
|
||||
assert response.json['result'] == 1
|
||||
assert models.Origin.objects.count() == 1
|
||||
|
@ -93,10 +97,8 @@ def test_push_document(app, admin_user, john_doe):
|
|||
assert models.UserDocument.objects.get().deletable_by_user is True
|
||||
assert models.Document.objects.count() == 1
|
||||
assert models.Document.objects.get().content.read() == b'coin'
|
||||
assert (models.UserDocument.objects.get().document
|
||||
== models.Document.objects.get())
|
||||
assert (models.UserDocument.objects.get().origin
|
||||
== models.Origin.objects.get())
|
||||
assert models.UserDocument.objects.get().document == models.Document.objects.get()
|
||||
assert models.UserDocument.objects.get().origin == models.Origin.objects.get()
|
||||
|
||||
data['file_b64_content'] = base64.b64encode(b'coin2').decode()
|
||||
data['deletable_by_user'] = False
|
||||
|
|
|
@ -31,11 +31,7 @@ def test_cleanup(freezer, john_doe):
|
|||
|
||||
foo = Document.objects.create(content=ContentFile(b'foo', name='foo.txt'))
|
||||
bar = Document.objects.create(content=ContentFile(b'bar', name='bar.txt'))
|
||||
UserDocument.objects.create(user=john_doe,
|
||||
document=foo,
|
||||
filename='foo.txt',
|
||||
title='',
|
||||
description='')
|
||||
UserDocument.objects.create(user=john_doe, document=foo, filename='foo.txt', title='', description='')
|
||||
OAuth2TempFile.objects.create(document=bar, client=client, filename='bar.txt')
|
||||
|
||||
call_command('fargo-cleanup')
|
||||
|
|
|
@ -23,6 +23,7 @@ from fargo.fargo.models import Document
|
|||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def login(app, username='admin', password='admin', user=None):
|
||||
login_page = app.get('/login/')
|
||||
login_form = login_page.forms[0]
|
||||
|
@ -36,6 +37,7 @@ def login(app, username='admin', password='admin', user=None):
|
|||
assert resp.status_int == 302
|
||||
return app
|
||||
|
||||
|
||||
def test_document_delete(app):
|
||||
f = ContentFile(b'A test file, ez pz.', 'test_file.txt')
|
||||
doc = Document.objects.get_by_file(f)
|
||||
|
|
|
@ -34,7 +34,6 @@ pytestmark = pytest.mark.django_db
|
|||
|
||||
|
||||
class FakedResponse(mock.Mock):
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
|
||||
|
@ -42,8 +41,11 @@ class FakedResponse(mock.Mock):
|
|||
@pytest.fixture
|
||||
def oauth2_client():
|
||||
return OAuth2Client.objects.create(
|
||||
client_name='test_oauth2', client_id='client-id', client_secret='client-secret',
|
||||
redirect_uris='https://example.net/document https://doc.example.net/ https://example.com')
|
||||
client_name='test_oauth2',
|
||||
client_id='client-id',
|
||||
client_secret='client-secret',
|
||||
redirect_uris='https://example.net/document https://doc.example.net/ https://example.com',
|
||||
)
|
||||
|
||||
|
||||
def assert_error_redirect(url, error):
|
||||
|
@ -53,11 +55,7 @@ def assert_error_redirect(url, error):
|
|||
def test_get_document_oauth2(app, john_doe, oauth2_client, user_doc):
|
||||
login(app, user=john_doe)
|
||||
url = reverse('oauth2-authorize')
|
||||
params = {
|
||||
'client_secret': oauth2_client.client_secret,
|
||||
'response_type': 'code',
|
||||
'state': 'achipeachope'
|
||||
}
|
||||
params = {'client_secret': oauth2_client.client_secret, 'response_type': 'code', 'state': 'achipeachope'}
|
||||
# test missing redirect_uri
|
||||
resp = app.get(url, params={}, status=400)
|
||||
assert resp.text == 'missing redirect_uri parameter'
|
||||
|
@ -133,7 +131,8 @@ def test_put_document(app, john_doe, oauth2_client):
|
|||
filename = 'éléphant.txt'
|
||||
percent_encode_filename = quote(filename, safe='')
|
||||
headers = {
|
||||
'Content-disposition': 'attachment; filename="%s"; filename*=UTF-8\'\'%s' % (filename, percent_encode_filename)
|
||||
'Content-disposition': 'attachment; filename="%s"; filename*=UTF-8\'\'%s'
|
||||
% (filename, percent_encode_filename)
|
||||
}
|
||||
|
||||
assert len(OAuth2TempFile.objects.all()) == 0
|
||||
|
@ -162,15 +161,16 @@ def test_put_document(app, john_doe, oauth2_client):
|
|||
assert OAuth2TempFile.objects.count() == 1
|
||||
assert UserDocument.objects.count() == 1
|
||||
assert OAuth2TempFile.objects.get().document == UserDocument.objects.get().document
|
||||
assert UserDocument.objects.filter(user=john_doe, document=doc.document, filename=u'éléphant.txt').exists()
|
||||
assert UserDocument.objects.filter(
|
||||
user=john_doe, document=doc.document, filename=u'éléphant.txt'
|
||||
).exists()
|
||||
|
||||
|
||||
def test_confirm_put_document_file_exception(app, oauth2_client, john_doe, user_doc):
|
||||
login(app, user=john_doe)
|
||||
oauth_tmp_file = OAuth2TempFile.objects.create(
|
||||
client=oauth2_client,
|
||||
document=user_doc.document,
|
||||
filename=user_doc.filename)
|
||||
client=oauth2_client, document=user_doc.document, filename=user_doc.filename
|
||||
)
|
||||
|
||||
url = reverse('oauth2-put-document-authorize', kwargs={'pk': 'fakemofo'})
|
||||
url += '?%s' % urlencode({'redirect_uri': 'https://example.com'})
|
||||
|
@ -192,7 +192,7 @@ def test_idp_authentication(mocked_post, settings, app, oauth2_client, john_doe,
|
|||
'client_secret': 'fake',
|
||||
'response_type': 'code',
|
||||
'state': 'achipeachope',
|
||||
'redirect': 'https://example.com/'
|
||||
'redirect': 'https://example.com/',
|
||||
}
|
||||
params['redirect_uri'] = 'https://example.com'
|
||||
resp = app.get(url, params=params)
|
||||
|
@ -213,9 +213,7 @@ def test_idp_authentication(mocked_post, settings, app, oauth2_client, john_doe,
|
|||
resp.json['detail'] == 'Invalid client_id/client_secret.'
|
||||
# when remote idp fails to authenticate rp
|
||||
settings.FARGO_IDP_URL = 'https://idp.example.org'
|
||||
response = {
|
||||
"result": 0, "errors": ["Invalid username/password."]
|
||||
}
|
||||
response = {"result": 0, "errors": ["Invalid username/password."]}
|
||||
mocked_post.return_value = FakedResponse(content=json.dumps(response))
|
||||
resp = app.post(url, params=params, status=401)
|
||||
resp.json['detail'] == 'Invalid client_id/client_secret.'
|
||||
|
@ -239,11 +237,9 @@ def test_command_create_client(db):
|
|||
|
||||
OAuth2Client.objects.all().delete()
|
||||
|
||||
call_command('oauth2-create-client',
|
||||
'test',
|
||||
'https://example.com/',
|
||||
'--client-id=wtf',
|
||||
'--client-secret=whocares')
|
||||
call_command(
|
||||
'oauth2-create-client', 'test', 'https://example.com/', '--client-id=wtf', '--client-secret=whocares'
|
||||
)
|
||||
client = OAuth2Client.objects.get()
|
||||
assert client.client_name == 'test'
|
||||
assert client.redirect_uris == 'https://example.com/'
|
||||
|
|
Loading…
Reference in New Issue