diff --git a/mayan/apps/django_gpg/apps.py b/mayan/apps/django_gpg/apps.py index 791df1c8be..b3abb60d18 100644 --- a/mayan/apps/django_gpg/apps.py +++ b/mayan/apps/django_gpg/apps.py @@ -14,7 +14,8 @@ from navigation import SourceColumn from .classes import KeyStub from .links import ( link_key_delete, link_key_detail, link_key_download, link_key_query, - link_key_receive, link_key_setup, link_private_keys, link_public_keys + link_key_receive, link_key_setup, link_key_upload, link_private_keys, + link_public_keys ) from .permissions import ( permission_key_delete, permission_key_download, permission_key_sign, @@ -104,7 +105,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ) ) menu_sidebar.bind_links( - links=(link_key_query,), + links=(link_key_query, link_key_upload), sources=( 'django_gpg:key_public_list', 'django_gpg:key_private_list', 'django_gpg:key_query', 'django_gpg:key_query_results', Key, diff --git a/mayan/apps/django_gpg/links.py b/mayan/apps/django_gpg/links.py index 22771fe351..70ea4ec69b 100644 --- a/mayan/apps/django_gpg/links.py +++ b/mayan/apps/django_gpg/links.py @@ -6,17 +6,9 @@ from navigation import Link from .permissions import ( permission_key_delete, permission_key_download, permission_key_receive, - permission_key_view, permission_keyserver_query + permission_key_view, permission_key_upload, permission_keyserver_query ) -link_private_keys = Link( - permissions=(permission_key_view,), text=_('Private keys'), - view='django_gpg:key_private_list' -) -link_public_keys = Link( - permissions=(permission_key_view,), text=_('Public keys'), - view='django_gpg:key_public_list' -) link_key_delete = Link( permissions=(permission_key_delete,), tags='dangerous', text=_('Delete'), view='django_gpg:key_delete', args=('resolved_object.pk',) @@ -41,3 +33,15 @@ link_key_setup = Link( icon='fa fa-key', permissions=(permission_key_view,), text=_('Key management'), view='django_gpg:key_public_list' ) +link_key_upload = Link( + permissions=(permission_key_upload,), text=_('Upload key'), + view='django_gpg:key_upload' +) +link_private_keys = Link( + permissions=(permission_key_view,), text=_('Private keys'), + view='django_gpg:key_private_list' +) +link_public_keys = Link( + permissions=(permission_key_view,), text=_('Public keys'), + view='django_gpg:key_public_list' +) diff --git a/mayan/apps/django_gpg/migrations/0001_initial.py b/mayan/apps/django_gpg/migrations/0001_initial.py index df1505e689..68e0aa0e31 100644 --- a/mayan/apps/django_gpg/migrations/0001_initial.py +++ b/mayan/apps/django_gpg/migrations/0001_initial.py @@ -13,16 +13,45 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Key', 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 + ) + ), ('data', models.TextField(verbose_name='Data')), - ('key_id', models.CharField(unique=True, max_length=16, verbose_name='Key ID')), - ('creation_date', models.DateField(verbose_name='Creation date')), - ('expiration_date', models.DateField(null=True, verbose_name='Expiration date', blank=True)), - ('fingerprint', models.CharField(unique=True, max_length=40, verbose_name='Fingerprint')), + ( + 'key_id', models.CharField( + unique=True, max_length=16, verbose_name='Key ID' + ) + ), + ( + 'creation_date', models.DateField( + verbose_name='Creation date' + ) + ), + ( + 'expiration_date', models.DateField( + null=True, verbose_name='Expiration date', blank=True + ) + ), + ( + 'fingerprint', models.CharField( + unique=True, max_length=40, verbose_name='Fingerprint' + ) + ), ('length', models.PositiveIntegerField(verbose_name='Length')), - ('algorithm', models.PositiveIntegerField(verbose_name='Algorithm')), + ( + 'algorithm', models.PositiveIntegerField( + verbose_name='Algorithm' + ) + ), ('user_id', models.TextField(verbose_name='User ID')), - ('key_type', models.CharField(max_length=3, verbose_name='Type')), + ( + 'key_type', models.CharField( + max_length=3, verbose_name='Type' + ) + ), ], options={ 'verbose_name': 'Key', diff --git a/mayan/apps/django_gpg/migrations/0003_auto_20160322_1810.py b/mayan/apps/django_gpg/migrations/0003_auto_20160322_1810.py index 40689f948e..c7a62e5450 100644 --- a/mayan/apps/django_gpg/migrations/0003_auto_20160322_1810.py +++ b/mayan/apps/django_gpg/migrations/0003_auto_20160322_1810.py @@ -14,22 +14,32 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='key', name='algorithm', - field=models.PositiveIntegerField(verbose_name='Algorithm', editable=False), + field=models.PositiveIntegerField( + verbose_name='Algorithm', editable=False + ), ), migrations.AlterField( model_name='key', name='creation_date', - field=models.DateField(verbose_name='Creation date', editable=False), + field=models.DateField( + verbose_name='Creation date', editable=False + ), ), migrations.AlterField( model_name='key', name='expiration_date', - field=models.DateField(verbose_name='Expiration date', null=True, editable=False, blank=True), + field=models.DateField( + verbose_name='Expiration date', null=True, editable=False, + blank=True + ), ), migrations.AlterField( model_name='key', name='fingerprint', - field=models.CharField(verbose_name='Fingerprint', unique=True, max_length=40, editable=False), + field=models.CharField( + verbose_name='Fingerprint', unique=True, max_length=40, + editable=False + ), ), migrations.AlterField( model_name='key', @@ -39,17 +49,24 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='key', name='key_id', - field=models.CharField(verbose_name='Key ID', unique=True, max_length=16, editable=False), + field=models.CharField( + verbose_name='Key ID', unique=True, max_length=16, + editable=False + ), ), migrations.AlterField( model_name='key', name='key_type', - field=models.CharField(verbose_name='Type', max_length=3, editable=False), + field=models.CharField( + verbose_name='Type', max_length=3, editable=False + ), ), migrations.AlterField( model_name='key', name='length', - field=models.PositiveIntegerField(verbose_name='Length', editable=False), + field=models.PositiveIntegerField( + verbose_name='Length', editable=False + ), ), migrations.AlterField( model_name='key', diff --git a/mayan/apps/django_gpg/migrations/0004_auto_20160322_2202.py b/mayan/apps/django_gpg/migrations/0004_auto_20160322_2202.py index a2bea9e955..d4d24ecc20 100644 --- a/mayan/apps/django_gpg/migrations/0004_auto_20160322_2202.py +++ b/mayan/apps/django_gpg/migrations/0004_auto_20160322_2202.py @@ -19,6 +19,9 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='key', name='key_type', - field=models.CharField(verbose_name='Type', max_length=3, editable=False, choices=[('pub', 'Public'), ('sec', 'Secret')]), + field=models.CharField( + verbose_name='Type', max_length=3, editable=False, + choices=[('pub', 'Public'), ('sec', 'Secret')] + ), ), ] diff --git a/mayan/apps/django_gpg/models.py b/mayan/apps/django_gpg/models.py index 7bb4d100de..de4f3e9528 100644 --- a/mayan/apps/django_gpg/models.py +++ b/mayan/apps/django_gpg/models.py @@ -43,7 +43,10 @@ def gpg_command(function): @python_2_unicode_compatible class Key(models.Model): - key_data = models.TextField(verbose_name=_('Key data')) + key_data = models.TextField( + help_text=_('ASCII armored version of the key.'), + verbose_name=_('Key data') + ) creation_date = models.DateField( editable=False, verbose_name=_('Creation date') ) @@ -80,7 +83,10 @@ class Key(models.Model): import_results = gpg_command(function=import_key) if not import_results.count: - raise ValidationError('Invalid key data') + raise ValidationError(_('Invalid key data')) + + if Key.objects.filter(fingerprint=import_results.fingerprints[0]).exists(): + raise ValidationError(_('Key already exists.')) def get_absolute_url(self): return reverse('django_gpg:key_detail', args=(self.pk,)) diff --git a/mayan/apps/django_gpg/permissions.py b/mayan/apps/django_gpg/permissions.py index 2b224a5348..5e36afadb9 100644 --- a/mayan/apps/django_gpg/permissions.py +++ b/mayan/apps/django_gpg/permissions.py @@ -18,6 +18,9 @@ permission_key_receive = namespace.add_permission( permission_key_sign = namespace.add_permission( name='key_sign', label=_('Use keys to sign content') ) +permission_key_upload = namespace.add_permission( + name='key_upload', label=_('Upload keys') +) permission_key_view = namespace.add_permission( name='key_view', label=_('View keys') ) diff --git a/mayan/apps/django_gpg/tests/test_models.py b/mayan/apps/django_gpg/tests/test_models.py index d95a4d78e4..afc59b692a 100644 --- a/mayan/apps/django_gpg/tests/test_models.py +++ b/mayan/apps/django_gpg/tests/test_models.py @@ -128,7 +128,7 @@ class KeyTestCase(TestCase): with self.assertRaises(NeedPassphrase): with open(TEST_FILE) as test_file: - detached_signature = key.sign_file( + key.sign_file( file_object=test_file, detached=True, ) @@ -137,7 +137,7 @@ class KeyTestCase(TestCase): with self.assertRaises(PassphraseError): with open(TEST_FILE) as test_file: - detached_signature = key.sign_file( + key.sign_file( file_object=test_file, detached=True, passphrase='bad passphrase' ) diff --git a/mayan/apps/django_gpg/tests/test_views.py b/mayan/apps/django_gpg/tests/test_views.py index ac281526ca..7f65c1329e 100644 --- a/mayan/apps/django_gpg/tests/test_views.py +++ b/mayan/apps/django_gpg/tests/test_views.py @@ -1,27 +1,19 @@ from __future__ import absolute_import, unicode_literals -from django.core.files import File - from django_downloadview.test import assert_download_response -from documents.models import Document, DocumentVersion -from documents.tests.literals import TEST_DOCUMENT_PATH -from documents.tests.test_views import GenericDocumentViewTestCase +from common.tests.test_views import GenericViewTestCase from user_management.tests import ( TEST_USER_USERNAME, TEST_USER_PASSWORD ) from ..models import Key -from ..permissions import permission_key_download +from ..permissions import permission_key_download, permission_key_upload -from .literals import ( - TEST_DETACHED_SIGNATURE, TEST_FILE, TEST_KEY_DATA, TEST_KEY_FINGERPRINT, - TEST_KEY_PASSPHRASE, TEST_SEARCH_FINGERPRINT, TEST_SEARCH_UID, - TEST_SIGNED_FILE, TEST_SIGNED_FILE_CONTENT -) +from .literals import TEST_KEY_DATA, TEST_KEY_FINGERPRINT -class KeyViewTestCase(GenericDocumentViewTestCase): +class KeyViewTestCase(GenericViewTestCase): def test_key_download_view_no_permission(self): key = Key.objects.create(key_data=TEST_KEY_DATA) @@ -48,3 +40,28 @@ class KeyViewTestCase(GenericDocumentViewTestCase): self, response=response, content=key.key_data, basename=key.key_id, ) + + def test_key_upload_view_no_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + response = self.post( + viewname='django_gpg:key_upload', data={'key_data': TEST_KEY_DATA} + ) + + self.assertEqual(response.status_code, 403) + self.assertEqual(Key.objects.count(), 0) + + def test_key_upload_view_with_permission(self): + self.login(username=TEST_USER_USERNAME, password=TEST_USER_PASSWORD) + + self.role.permissions.add(permission_key_upload.stored_permission) + + response = self.post( + viewname='django_gpg:key_upload', data={'key_data': TEST_KEY_DATA}, + follow=True + ) + + self.assertContains(response, 'created', status_code=200) + + self.assertEqual(Key.objects.count(), 1) + self.assertEqual(Key.objects.first().fingerprint, TEST_KEY_FINGERPRINT) diff --git a/mayan/apps/django_gpg/urls.py b/mayan/apps/django_gpg/urls.py index e9530136fb..929e613e71 100644 --- a/mayan/apps/django_gpg/urls.py +++ b/mayan/apps/django_gpg/urls.py @@ -4,7 +4,8 @@ from django.conf.urls import patterns, url from .views import ( KeyDeleteView, KeyDetailView, KeyDownloadView, KeyQueryView, - KeyQueryResultView, KeyReceive, PrivateKeyListView, PublicKeyListView + KeyQueryResultView, KeyReceive, KeyUploadView, PrivateKeyListView, + PublicKeyListView ) urlpatterns = patterns( @@ -26,6 +27,9 @@ urlpatterns = patterns( url( r'^list/public/$', PublicKeyListView.as_view(), name='key_public_list' ), + url( + r'^upload/$', KeyUploadView.as_view(), name='key_upload' + ), url(r'^query/$', KeyQueryView.as_view(), name='key_query'), url( r'^query/results/$', KeyQueryResultView.as_view(), diff --git a/mayan/apps/django_gpg/views.py b/mayan/apps/django_gpg/views.py index 8a40f822d0..4e6591640a 100644 --- a/mayan/apps/django_gpg/views.py +++ b/mayan/apps/django_gpg/views.py @@ -8,8 +8,9 @@ from django.core.urlresolvers import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ from common.generics import ( - ConfirmView, SimpleView, SingleObjectDeleteView, SingleObjectDetailView, - SingleObjectDownloadView, SingleObjectListView + ConfirmView, SingleObjectCreateView, SingleObjectDeleteView, + SingleObjectDetailView, SingleObjectDownloadView, SingleObjectListView, + SimpleView ) from .forms import KeyDetailForm, KeySearchForm @@ -17,7 +18,7 @@ from .literals import KEY_TYPE_PUBLIC from .models import Key from .permissions import ( permission_key_delete, permission_key_download, permission_key_receive, - permission_key_view, permission_keyserver_query + permission_key_upload, permission_key_view, permission_keyserver_query ) logger = logging.getLogger(__name__) @@ -126,6 +127,18 @@ class KeyQueryResultView(SingleObjectListView): return () +class KeyUploadView(SingleObjectCreateView): + fields = ('key_data',) + model = Key + post_action_redirect = reverse_lazy('django_gpg:key_public_list') + view_permission = permission_key_upload + + def get_extra_context(self): + return { + 'title': _('Upload new key'), + } + + class PublicKeyListView(SingleObjectListView): object_permission = permission_key_view queryset = Key.objects.public_keys()