From 3a003ae39b412b0c1fa81837cae502dd80e97e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arno=20T=C3=B6ll?= Date: Mon, 16 Apr 2012 21:24:34 -0400 Subject: [PATCH 01/12] Add a SSL hint to the login form --- debexpo/controllers/login.py | 2 ++ debexpo/templates/login/index.mako | 4 ++++ development.ini | 2 ++ 3 files changed, 8 insertions(+) diff --git a/debexpo/controllers/login.py b/debexpo/controllers/login.py index 8ee07cdb..92089e51 100644 --- a/debexpo/controllers/login.py +++ b/debexpo/controllers/login.py @@ -110,6 +110,8 @@ class LoginController(BaseController): If True, display the form even if request.method is POST. """ + + c.request = request if request.method == 'POST' and get is False: log.debug('Login form submitted with email = "%s"' % request.POST.get('email')) return self._login() diff --git a/debexpo/templates/login/index.mako b/debexpo/templates/login/index.mako index 7413fdb8..9613a144 100644 --- a/debexpo/templates/login/index.mako +++ b/debexpo/templates/login/index.mako @@ -32,6 +32,10 @@ ${ h.html.tags.end_form() } +% if c.request and c.request.scheme == 'http': +

Secure log-in: ${ _('Switch to SSL') }

+% endif +

${ _('Did you lose your password?')} ${ _('Try resetting your password.')}

diff --git a/development.ini b/development.ini index 2e6dccf1..633ef48a 100644 --- a/development.ini +++ b/development.ini @@ -16,6 +16,8 @@ error_email_from = paste@localhost use = egg:Paste#http host = 0.0.0.0 port = 5000 +# uncomment to enable SSL +# ssl_pem = * [app:main] use = egg:debexpo -- GitLab From 46bb0d5d267dafff2196746914f39e659ece53a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arno=20T=C3=B6ll?= Date: Tue, 17 Apr 2012 15:13:23 -0400 Subject: [PATCH 02/12] Don't raise an exception on socket errors, try it again next time. The code is robust enough to catch that error --- debexpo/cronjobs/removeolduploads.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/debexpo/cronjobs/removeolduploads.py b/debexpo/cronjobs/removeolduploads.py index 73f0c49e..a47ceb4c 100644 --- a/debexpo/cronjobs/removeolduploads.py +++ b/debexpo/cronjobs/removeolduploads.py @@ -45,6 +45,7 @@ from debexpo.model.data_store import DataStore from debexpo.model import meta from debian import deb822 +import socket import re import apt_pkg import datetime @@ -124,7 +125,12 @@ class RemoveOldUploads(BaseCronjob): self.mailer.disconnect_from_server() def invoke(self): - self._remove_uploaded_packages() + try: + self._remove_uploaded_packages() + except socket.error as e: + # better luck next time + self.log.debug("Socket error %s: skipping removals his time" % (e)) + pass # We don't need to run our garbage collection of old cruft that often # It's ok if we purge old packages once a day. -- GitLab From 74eebfe9dfa416aad27520bd03a109527019e779 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Wed, 18 Apr 2012 18:37:46 +0200 Subject: [PATCH 03/12] Remove stale files if their mtime is older than 6 hours --- debexpo/cronjobs/importuploads.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/debexpo/cronjobs/importuploads.py b/debexpo/cronjobs/importuploads.py index 0561d40a..ef457b16 100644 --- a/debexpo/cronjobs/importuploads.py +++ b/debexpo/cronjobs/importuploads.py @@ -40,6 +40,7 @@ import glob import os import os.path import subprocess +import time import datetime import shutil @@ -53,7 +54,6 @@ class ImportUpload(BaseCronjob): """ This method does nothing in this cronjob """ - self.stale_files = [] self.files = debexpo.lib.filesystem.CheckFiles() self.log.debug("%s loaded successfully" % (__name__)) @@ -111,31 +111,18 @@ class ImportUpload(BaseCronjob): # 2) Mark unprocessed files and get rid of them after some time pub = os.path.join(self.config['debexpo.upload.incoming'], "pub") - filenames = [name for (name, _) in self.stale_files] - file_to_check = [] for file in glob.glob( os.path.join(pub, '*') ): if self.files.allowed_upload(file): self.log.debug("Incomplete upload: %s" % (file)) - if not file in filenames: - self.stale_files.append((file,datetime.datetime.now())) - else: - file_to_check.append(file) + last_change = time.time() - os.stat(file).st_mtime + # the file was uploaded more than 6 hours ago + if last_change > 6 * 60 * 60: + self.log.warning("Remove old file: %s (last modified %.2f hours ago)" % (file, last_change / 3600.)) + os.remove(file) else: if os.path.isfile(file): self.log.warning("Remove unknown file: %s" % (file)) os.remove(file) - new_file_list = [] - for file in file_to_check: - for (file_known, last_check) in self.stale_files: - if file == file_known and (datetime.datetime.now() - last_check) > datetime.timedelta(hours = 6): - if os.path.isfile(file): - self.log.warning("Remove incomplete upload: %s" % (file)) - os.remove(file) - continue - new_file_list.append((file_known, last_check)) - - self.stale_files = new_file_list - cronjob = ImportUpload schedule = datetime.timedelta(minutes = 10) -- GitLab From 80e8ae1905dd5e16d32e258eb0bd659731880bed Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Thu, 19 Apr 2012 19:48:05 +0200 Subject: [PATCH 04/12] Remove the PPA-related tests --- debexpo/tests/functional/test_ppa.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 debexpo/tests/functional/test_ppa.py diff --git a/debexpo/tests/functional/test_ppa.py b/debexpo/tests/functional/test_ppa.py deleted file mode 100644 index fc4a3121..00000000 --- a/debexpo/tests/functional/test_ppa.py +++ /dev/null @@ -1,7 +0,0 @@ -from debexpo.tests import * - -class TestPpaController(TestController): - - def test_index(self): - response = self.app.get(url(controller='ppa', action='index')) - # Test response... -- GitLab From 70bc4d573ac231a612fd8e1000323a5502f2fee0 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Thu, 19 Apr 2012 21:41:20 +0200 Subject: [PATCH 05/12] Changes doesn't have a filename if generated from a string --- debexpo/lib/changes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/debexpo/lib/changes.py b/debexpo/lib/changes.py index 0d3fa6eb..a9814bb9 100644 --- a/debexpo/lib/changes.py +++ b/debexpo/lib/changes.py @@ -74,7 +74,10 @@ class Changes(object): if len(self._data) == 0: raise Exception('Changes file could not be parsed.') - self.basename = os.path.basename(filename) + if filename: + self.basename = os.path.basename(filename) + else: + self.basename = None def get_filename(self): """ -- GitLab From 166752206c45661b1922d514dc469f4feafaf55e Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Thu, 19 Apr 2012 21:42:24 +0200 Subject: [PATCH 06/12] Purge the session key at every login --- debexpo/controllers/login.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debexpo/controllers/login.py b/debexpo/controllers/login.py index 92089e51..735dfe32 100644 --- a/debexpo/controllers/login.py +++ b/debexpo/controllers/login.py @@ -44,6 +44,8 @@ from debexpo.lib.schemas import LoginForm from debexpo.model import meta from debexpo.model.users import User +import debexpo.model + import debexpo.lib.utils log = logging.getLogger(__name__) @@ -99,6 +101,13 @@ class LoginController(BaseController): else: path = url('my') + # Purge the session upload key + keys = meta.session.query(debexpo.model.user_upload_key.UserUploadKey + ).filter_by(user=u) + if keys: + for key in keys: + meta.session.delete(key) + meta.session.commit() redirect(path) -- GitLab From 590b38e4a7bebd06ae28a19629323945adba8add Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Thu, 19 Apr 2012 21:42:53 +0200 Subject: [PATCH 07/12] Do not fail if the package has no files --- debexpo/lib/filesystem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debexpo/lib/filesystem.py b/debexpo/lib/filesystem.py index 016ee447..ad4eba2b 100644 --- a/debexpo/lib/filesystem.py +++ b/debexpo/lib/filesystem.py @@ -174,6 +174,8 @@ class CheckFiles(object): ```package``` package object whose files are supposed to be removed """ files = self.find_files_for_package(package, absolute_path=True) + if not files: + return path = os.path.dirname(files[0]) for file in files: if os.path.exists(file): -- GitLab From 884ad239946e9e21d8b149cef1ec995718a38030 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Thu, 19 Apr 2012 21:44:48 +0200 Subject: [PATCH 08/12] allowed_upload moved to debexpo.lib.filesystem --- debexpo/tests/test_filesystem.py | 64 ++++++++++++++++++++++++++++++++ debexpo/tests/test_utils.py | 15 -------- 2 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 debexpo/tests/test_filesystem.py diff --git a/debexpo/tests/test_filesystem.py b/debexpo/tests/test_filesystem.py new file mode 100644 index 00000000..bab3a97c --- /dev/null +++ b/debexpo/tests/test_filesystem.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# test_filesystem.py — CheckFiles class test cases +# +# This file is part of debexpo - https://alioth.debian.org/projects/debexpo/ +# +# Copyright © 2012 Nicolas Dandrimont +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +""" +CheckFiles class test cases. +""" + +__author__ = 'Nicolas Dandrimont' +__copyright__ = 'Copyright © 2012 Nicolas Dandrimont' +__license__ = 'MIT' + +from unittest import TestCase + +from debexpo.lib.filesystem import CheckFiles + +class TestCheckFilesController(TestCase): + + def setUp(self): + """ + Setup the environment for tests + """ + self.checkfiles = CheckFiles() + + def testAllowedUpload(self): + """ + Tests CheckFiles.allowed_upload + """ + t = self.checkfiles.allowed_upload + + self.assertTrue(t('foo_version.orig.tar.gz')) + self.assertTrue(t('foo_version.tar.gz')) + self.assertTrue(t('foo_version.changes')) + self.assertTrue(t('foo_version.dsc')) + self.assertTrue(t('foo_version.deb')) + self.assertTrue(t('foo_version.diff.gz')) + + self.assertFalse(t('foo_version.etc')) + diff --git a/debexpo/tests/test_utils.py b/debexpo/tests/test_utils.py index 097d7259..85899347 100644 --- a/debexpo/tests/test_utils.py +++ b/debexpo/tests/test_utils.py @@ -42,21 +42,6 @@ from debexpo.lib.changes import Changes class TestUtilsController(TestCase): - def testAllowedUpload(self): - """ - Tests debexpo.lib.utils.allowed_upload. - """ - t = allowed_upload - - self.assertTrue(t('foo_version.orig.tar.gz')) - self.assertTrue(t('foo_version.tar.gz')) - self.assertTrue(t('foo_version.changes')) - self.assertTrue(t('foo_version.dsc')) - self.assertTrue(t('foo_version.deb')) - self.assertTrue(t('foo_version.diff.gz')) - - self.assertFalse(t('foo_version.etc')) - def testParseSection(self): """ Tests debexpo.lib.utils.parse_section. -- GitLab From 25cce31cfc22e8bb64185b998b387e9895d094ff Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Thu, 19 Apr 2012 22:13:13 +0200 Subject: [PATCH 09/12] Fix most of the tests The gnupg-related tests are still failing, but that's a start --- debexpo/tests/functional/test_index.py | 8 +- debexpo/tests/functional/test_my.py | 79 ++++++++++++++-- debexpo/tests/functional/test_package.py | 36 ++++---- debexpo/tests/functional/test_packages.py | 5 ++ debexpo/tests/functional/test_register.py | 10 ++- debexpo/tests/functional/test_upload.py | 104 ++++++++-------------- test.ini | 55 ++++++++++-- 7 files changed, 192 insertions(+), 105 deletions(-) diff --git a/debexpo/tests/functional/test_index.py b/debexpo/tests/functional/test_index.py index 6f3f45fc..e361c0de 100644 --- a/debexpo/tests/functional/test_index.py +++ b/debexpo/tests/functional/test_index.py @@ -27,7 +27,7 @@ class TestIndexController(TestController): response = self.app.get(testurl) self.assertEquals(response.status_int, 200) - testtext = '

A test front page

' + testtext = '

Welcome to debexpo

' pylons.test.pylonsapp.config['debexpo.html.frontpage'] = \ self._generate_temppage('front.html', testtext) @@ -46,7 +46,7 @@ class TestIndexController(TestController): response = self.app.get(testurl) self.assertEquals(response.status_int, 200) - testtext = '

A maintainer intro page

' + testtext = '

Introduction for maintainers: How will my package get into Debian

' pylons.test.pylonsapp.config['debexpo.html.maintainer_intro'] = \ self._generate_temppage('maintainer_intro.html', testtext) @@ -57,11 +57,11 @@ class TestIndexController(TestController): del pylons.test.pylonsapp.config['debexpo.html.maintainer_intro'] def test_intro_sponsors(self): - testurl = url('intro-sponsors') + testurl = url('sponsors') response = self.app.get(testurl) self.assertEquals(response.status_int, 200) - testtext = '

A sponsor intro page

' + testtext = '

The sponsoring process

' pylons.test.pylonsapp.config['debexpo.html.sponsors_intro'] = \ self._generate_temppage('sponsor_intro.html', testtext) diff --git a/debexpo/tests/functional/test_my.py b/debexpo/tests/functional/test_my.py index 9e1f1453..4a18ec26 100644 --- a/debexpo/tests/functional/test_my.py +++ b/debexpo/tests/functional/test_my.py @@ -6,7 +6,7 @@ from debexpo.model.user_countries import UserCountry import md5 class TestMyController(TestController): - _GPGKEY = """-----BEGIN PGP PUBLIC KEY BLOCK----- + _LOW_STRENGTH_GPGKEY = """-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) mQENBEwmV4wBCADCbdBf65H0r13XfVVncCc9pW7XkDYuKD8locXY48IdKVQRKK97 @@ -37,7 +37,62 @@ ABRL0EeYuGCJYJRQsw8e8JuRSaVGwfotqkIHtQ== =PXiv -----END PGP PUBLIC KEY BLOCK----- """ - _GPG_ID = '2048R/6758261E' + _LOW_STRENGTH_GPG_ID = '2048R/6758261E' + + _HIGH_STRENGTH_GPGKEY = """-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.12 (GNU/Linux) + +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 +=fw7J +-----END PGP PUBLIC KEY BLOCK----- +""" + + _HIGH_STRENGTH_GPG_ID = '4096R/AC4ABA99' def setUp(self): self._setup_models() @@ -100,22 +155,34 @@ ABRL0EeYuGCJYJRQsw8e8JuRSaVGwfotqkIHtQ== user = meta.session.query(User).filter(User.email=='email@example.com').one() self.assertEquals(user.gpg, None) - # upload GPG key + # upload GPG key with low strength + response = self.app.post(url('my'), {'form': 'gpg', + 'delete_gpg': 0, + 'commit': 'submit'}, + upload_files = [('gpg', 'mykey.asc', + self._LOW_STRENGTH_GPGKEY)]) + self.assertEquals(response.status_int, 200) + low_strength_msg = 'Key strength unacceptable in Debian Keyring. The minimum required key strength is' + self.assertTrue(low_strength_msg in response) + user = meta.session.query(User).filter(User.email=='email@example.com').one() + self.assertEquals(user.gpg, None) + + # upload GPG key with high strength response = self.app.post(url('my'), {'form': 'gpg', 'delete_gpg': 0, 'commit': 'submit'}, upload_files = [('gpg', 'mykey.asc', - self._GPGKEY)]) + self._HIGH_STRENGTH_GPGKEY)]) self.assertEquals(response.status_int, 302) self.assertTrue(response.location.endswith(url('my'))) user = meta.session.query(User).filter(User.email=='email@example.com').one() - self.assertEquals(user.gpg, self._GPGKEY) + self.assertEquals(user.gpg, self._HIGH_STRENGTH_GPGKEY) # test whether index page contains GPG delete link response = self.app.get(url(controller='my', action='index')) self.assertEquals(response.status_int, 200) self.assertTrue('' % (url('logout')) in response) - self.assertTrue(self._GPG_ID in response) + self.assertTrue(self._HIGH_STRENGTH_GPG_ID in response) # delete GPG key response = self.app.post(url('my'), {'form': 'gpg', diff --git a/debexpo/tests/functional/test_package.py b/debexpo/tests/functional/test_package.py index 18d7648b..1c8445fd 100644 --- a/debexpo/tests/functional/test_package.py +++ b/debexpo/tests/functional/test_package.py @@ -64,28 +64,19 @@ class TestPackageController(TestController): 'packages-uploader', id='email@example.com'))), 1) response = self.app.post(url('login'), self._AUTHDATA) + user = meta.session.query(User).filter(User.email=='email@example.com').one() response = self.app.get(url(controller='package', action='index', packagename='testpackage')) self.assertEquals(response.status_int, 200) self.assertEquals(len(response.lxml.xpath( '//a[@href="%s"]' % url( controller='package', action='delete', - packagename='testpackage'))), 1) + packagename='testpackage', key = user.get_upload_key()))), 1) self.assertEquals(len(response.lxml.xpath( '//form[@action="%s"]' % url( controller='package', action='comment', packagename='testpackage'))), 1) - def test_rfs(self): - response = self.app.get(url(controller='package', action='rfs')) - self.assertEquals(response.status_int, 302) - self.assertTrue(response.location.endswith( - url(controller='packages', action='index', packagename=None))) - response = self.app.get(url(controller='package', action='rfs', - packagename='testpackage')) - self.assertEquals(response.status_int, 200) - self.assertTrue('Subject: RFS: testpackage' in response) - def test_subscribe(self): response = self.app.get(url(controller='package', action='subscribe', packagename='testpackage')) @@ -157,16 +148,16 @@ class TestPackageController(TestController): self.assertEquals(subs, None) def test_delete(self): - response = self.app.get(url(controller='package', action='delete', - packagename='testpackage')) - self.assertEquals(response.status_int, 302) - self.assertTrue(response.location.endswith(url('login'))) self.app.post(url('login'), self._AUTHDATA) + user = meta.session.query(User).filter(User.email=='email@example.com').one() response = self.app.get(url(controller='package', action='delete', - packagename='testpackage')) + packagename='testpackage', key = "INVALIDKEY"), expect_errors=True) + self.assertEquals(response.status_int, 402) + response = self.app.get(url(controller='package', action='delete', + packagename='testpackage', key = user.get_upload_key())) self.assertEquals(response.status_int, 302) self.assertTrue(response.location.endswith( - url(controller='packages', action='index', filter='my'))) + url(controller='packages', action='my'))) package = meta.session.query(Package).filter( Package.name=='testpackage').first() self.assertEquals(package, None) @@ -175,13 +166,16 @@ class TestPackageController(TestController): response = self.app.get(url(controller='package', action='comment', packagename='testpackage')) self.assertEquals(response.status_int, 302) - self.assertTrue(response.location.endswith(url('login'))) + self.assertTrue(response.location.endswith( + url(controller='package', action='index', + packagename='testpackage'))) self.app.post(url('login'), self._AUTHDATA) response = self.app.get(url(controller='package', action='comment', packagename='testpackage')) - self.assertEquals(response.status_int, 200) - self.assertEquals(4, len(response.lxml.xpath( - '//span[@class="error-message"]'))) + self.assertTrue(response.location.endswith( + url(controller='package', action='index', + packagename='testpackage'))) + self.assertEquals(response.status_int, 302) response = self.app.post( url(controller='package', action='comment', packagename='testpackage'), diff --git a/debexpo/tests/functional/test_packages.py b/debexpo/tests/functional/test_packages.py index e3f094db..2123eb21 100644 --- a/debexpo/tests/functional/test_packages.py +++ b/debexpo/tests/functional/test_packages.py @@ -18,9 +18,14 @@ class TestPackagesController(TestController): self.assertEquals('text/html', response.content_type) def test_uploader(self): + response = self.app.get(url('packages-uploader', id='email@example.com'), expect_errors = True) + self.assertEquals(404, response.status_int) + self._setup_example_user() response = self.app.get(url('packages-uploader', id='email@example.com')) self.assertEquals(200, response.status_int) self.assertEquals('text/html', response.content_type) + self._remove_example_user() + def test_my(self): response = self.app.get(url(controller='packages', action='my')) diff --git a/debexpo/tests/functional/test_register.py b/debexpo/tests/functional/test_register.py index 0a2359e0..fc763dfa 100644 --- a/debexpo/tests/functional/test_register.py +++ b/debexpo/tests/functional/test_register.py @@ -8,12 +8,13 @@ class TestRegisterController(TestController): count = meta.session.query(User).filter(User.email=='mr_me@example.com').count() self.assertEquals(count, 0) - self.app.post(url(controller='register', action='maintainer'), + self.app.post(url(controller='register', action='register'), {'name': 'Mr. Me', 'password': 'password', 'password_confirm': 'password', 'commit': 'yes', - 'email': 'mr_me@example.com'}) + 'email': 'mr_me@example.com', + 'sponsor': '0'}) count = meta.session.query(User).filter(User.email=='mr_me@example.com').count() self.assertEquals(count, 1) @@ -28,12 +29,13 @@ class TestRegisterController(TestController): def test_maintainer_signup_with_duplicate_name(self): self.test_maintainer_signup(actually_delete_it=False) - self.app.post(url(controller='register', action='maintainer'), + self.app.post(url(controller='register', action='register'), {'name': 'Mr. Me', 'password': 'password', 'password_confirm': 'password', 'commit': 'yes', - 'email': 'mr_me_again@example.com'}) + 'email': 'mr_me_again@example.com', + 'sponsor': '0'}) count = meta.session.query(User).filter(User.email=='mr_me_again@example.com').count() self.assertEquals(count, 0) diff --git a/debexpo/tests/functional/test_upload.py b/debexpo/tests/functional/test_upload.py index baff7b83..a87fb40c 100644 --- a/debexpo/tests/functional/test_upload.py +++ b/debexpo/tests/functional/test_upload.py @@ -6,6 +6,7 @@ # # Copyright © 2008 Jonny Lamb # Copyright © 2010 Jan Dittberner +# Copyright © 2012 Nicolas Dandrimont # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation @@ -33,112 +34,85 @@ UploadController test cases. """ __author__ = 'Jonny Lamb' -__copyright__ = 'Copyright © 2008 Jonny Lamb, Copyright © 2010 Jan Dittberner' +__copyright__ = ', '.join([ + 'Copyright © 2008 Jonny Lamb', + 'Copyright © 2010 Jan Dittberner', + 'Copyright © 2012 Nicolas Dandrimont', + ]) __license__ = 'MIT' import os -import base64 +import shutil from debexpo.tests import TestController, url import pylons.test class TestUploadController(TestController): - def __init__(self, *args, **kwargs): - """ - Sets up database with data to provide a database to test. - """ - TestController.__init__(self, *args, **kwargs) - - # Keep this so tests don't have to constantly create it. - self.user_upload_key = 'upload_key' - self.email = 'email@example.com' - def setUp(self): self._setup_models() self._setup_example_user() + app_config = pylons.test.pylonsapp.config + shutil.rmtree(app_config['debexpo.upload.incoming']) + os.makedirs(os.path.join(app_config['debexpo.upload.incoming'], 'pub')) def tearDown(self): self._remove_example_user() + app_config = pylons.test.pylonsapp.config - def testGetRequest(self): + def test_get(self): """ Tests whether requests where method != PUT are rejected with error code 405. """ response = self.app.get(url(controller='upload', action='index', - email=self.email, password=self.user_upload_key, filename='testname.dsc'), expect_errors=True) self.assertEqual(response.status_int, 405) - def testNoAuthorization(self): - """ - Tests whether requests where the "Authorization" header is missing are rejected with - error code 401 and whether the "WWW-Authenticate" header is sent in the response with - the correct "realm" syntax. - """ - response = self.app.put( - url(controller='upload', action='index', - filename='testname.dsc', email='email', password='pass'), expect_errors=True) - - self.assertEqual(response.status_int, 403) - - def testFalseAuthentication(self): + def test_upload_wrong_extension(self): """ - Tests whether false authentication details returns a 403 error code. + Tests whether uploads of an unknown file extensions are rejected with error code 403. """ response = self.app.put(url(controller='upload', action='index', - filename='testname.dsc', email=self.email, - password='wrong'), + filename='testname.unknown'), expect_errors=True) self.assertEqual(response.status_int, 403) - def testTrueAuthentication(self): + def test_upload_successful(self): """ - Tests whether true authentication details returns a nicer error code. + Tests whether uploads with sane file extensions and authorization are successful. """ - response = self.app.put(url(controller='upload', action='index', - filename='testname.dsc', email=self.email, - password=self.user_upload_key), - expect_errors=False) + extensions = ('dsc', 'changes', 'deb', 'tar.gz', 'tar.bz2', 'tar.xz') - self.assertNotEqual(response.status_int, 403) - app_config = pylons.test.pylonsapp.config + for extension in extensions: + filename = 'testfile.%s' % extension + response = self.app.put(url(controller='upload', action='index', + filename=filename), + params='contents', expect_errors=False) - if os.path.isfile(os.path.join(app_config['debexpo.upload.incoming'], 'testfile1.dsc')): - os.remove(os.path.join(app_config['debexpo.upload.incoming'], 'testfile1.dsc')) + self.assertEqual(response.status_int, 200) - def testExtensionNotAllowed(self): - """ - Tests whether uploads of an unknown file extensions are rejected with error code 403. - """ - response = self.app.put(url(controller='upload', action='index', - filename='testname.unknown', email=self.email, - password=self.user_upload_key), - expect_errors=True) + app_config = pylons.test.pylonsapp.config + self.assertTrue(os.path.isfile(os.path.join(app_config['debexpo.upload.incoming'], 'pub', + filename))) - self.assertEqual(response.status_int, 403) + self.assertEqual(file(os.path.join(app_config['debexpo.upload.incoming'], 'pub', + filename)).read(), 'contents') - def testSuccessfulUpload(self): + def test_reupload_disallowed(self): """ - Tests whether uploads with sane file extensions and authorization are successful. + Tests whether uploading a file twice is disallowed. """ - response = self.app.put(url( - controller='upload', action='index', - filename='testfile2.dsc', - email=self.email, - password=self.user_upload_key), - params='contents', expect_errors=False) + filename = 'testfile.dsc' + response = self.app.put(url(controller='upload', action='index', + filename=filename), + params='contents', expect_errors=False) self.assertEqual(response.status_int, 200) - app_config = pylons.test.pylonsapp.config - self.assertTrue(os.path.isfile(os.path.join(app_config['debexpo.upload.incoming'], - 'testfile2.dsc'))) - - self.assertEqual(file(os.path.join(app_config['debexpo.upload.incoming'], - 'testfile2.dsc')).read(), 'contents') + response = self.app.put(url(controller='upload', action='index', + filename=filename), + params='contents', expect_errors=True) - if os.path.isfile(os.path.join(app_config['debexpo.upload.incoming'], 'testfile2.dsc')): - os.remove(os.path.join(app_config['debexpo.upload.incoming'], 'testfile2.dsc')) + self.assertEqual(response.status_int, 403) diff --git a/test.ini b/test.ini index 2a9a438e..f9b14851 100644 --- a/test.ini +++ b/test.ini @@ -8,12 +8,16 @@ debug = true # Uncomment and replace with the address which should receive any error reports #email_to = you@yourdomain.com smtp_server = localhost +smtp_username = +smtp_password = error_email_from = paste@localhost [server:main] use = egg:Paste#http host = 127.0.0.1 port = 5000 +# uncomment to enable SSL +# ssl_pem = * [app:main] use = egg:debexpo @@ -24,7 +28,7 @@ beaker.session.secret = somesecret sqlalchemy.url = sqlite:// # Directory name to add incoming uploaded files into -debexpo.upload.incoming = /tmp/debexpo/ +debexpo.upload.incoming = /tmp/debexpo/incoming/ # Directory name to store accepted uploaded files debexpo.repository = /tmp/debexpo/files/ @@ -35,6 +39,9 @@ debexpo.importer = %(here)s/bin/debexpo-importer # Whether to let debexpo handle the /debian/ directory debexpo.handle_debian = true +# Site title (e.g. short name) +debexpo.sitetitle = Mentors + # Site name debexpo.sitename = debexpo @@ -51,17 +58,55 @@ debexpo.email = email@example.org debexpo.debian_specific = true # What post-upload plugins to run, in this order -debexpo.plugins.post_upload = checkfiles +debexpo.plugins.post_upload = getorigtarball notuploader # What qa plugins to run, in this order -debexpo.plugins.qa = +debexpo.plugins.qa = lintian native maintaineremail watchfile closedbugs controlfields diffclean buildsystem debianqa distribution -# Path to the gpg binary -debexpo.gpg_path = /usr/bin/gpg +# What plugins to run when the package is uploaded to Debian, in this order +debexpo.plugins.post_upload_to_debian = removepackage + +# What plugins to run when a package is successfully uploaded, in this order +debexpo.plugins.post_successful_upload = changeslist + +# Extra plugin directory +debexpo.plugindir = /tmp + +# Location of the nearest Debian mirror +debexpo.debian_mirror = http://ftp.uk.debian.org/debian + +# Email address to send package accepts to +debexpo.changes_list = changes@example.com # Server debexpo is being run on including http:// and excluding trailing slash debexpo.server = http://localhost:5000 +# Path to the gpg binary +debexpo.gpg_path = /usr/bin/gpg + +# Path to the mentors keyring +debexpo.gpg_keyring = /tmp/debexpo-keyring.gpg + +# Minimum key strength required for the key to be acceptable in Debian keyring. +debexpo.gpg_minkeystrength = 4096 + +# Cronjobs to run by the Worker task +debexpo.cronjobs = removeolduploads importuploads + +# Extra plugin directory +debexpo.cronjobdir = /tmp + +# Debexpo cronjob Worker iteration delay. +# The worker will sleep that amount of seconds after each run +debexpo.cronjob_delay = 60 + +# NNTP server to connect to fetch mailing list comments/changes +debexpo.nntp_server = news.gmane.org + +# Enable experimental and/or broken code +debexpo.enable_experimental_code = true + +# Logging configuration [loggers] keys = root, debexpo -- GitLab From ca0dc5c2bc3af74642b3201293f091d3546d8e70 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Fri, 20 Apr 2012 00:23:24 +0200 Subject: [PATCH 10/12] Actually import the user_upload_key model --- debexpo/controllers/login.py | 1 + 1 file changed, 1 insertion(+) diff --git a/debexpo/controllers/login.py b/debexpo/controllers/login.py index 735dfe32..86c8f0be 100644 --- a/debexpo/controllers/login.py +++ b/debexpo/controllers/login.py @@ -45,6 +45,7 @@ from debexpo.model import meta from debexpo.model.users import User import debexpo.model +import debexpo.model.user_upload_key import debexpo.lib.utils -- GitLab From c2045e9249339fe7139b48bf3067bbbfcb393fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arno=20T=C3=B6ll?= Date: Sat, 5 May 2012 04:27:24 -0400 Subject: [PATCH 11/12] Spelling fix for templates/index/intro-maintainers.mako --- debexpo/templates/index/intro-maintainers.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debexpo/templates/index/intro-maintainers.mako b/debexpo/templates/index/intro-maintainers.mako index 22c82770..99cb74d6 100644 --- a/debexpo/templates/index/intro-maintainers.mako +++ b/debexpo/templates/index/intro-maintainers.mako @@ -13,7 +13,7 @@

2. File a WNPP bug

-

Work-Needing and Prospective Packages (WNPP) is our system of announcing your intent to markup packages being worked on. In particular it is a bug against the WNPP pseudo package (or use a nice frontend to browse WNPP bugs). If you want to package package something not currently available in Debian, the very first step should be to file an "Intent to package" (ITP) bug against WNPP. You may want to use the reportbug tool to achieve that by selecting "wnpp" as package to report a bug to.

+

Work-Needing and Prospective Packages (WNPP) is our system of announcing your intent to markup packages being worked on. In particular it is a bug against the WNPP pseudo package (or use a nice frontend to browse WNPP bugs). If you want to package something not currently available in Debian, the very first step should be to file an "Intent to package" (ITP) bug against WNPP. You may want to use the reportbug tool to achieve that by selecting "wnpp" as package to report a bug to.

3. Make the package

-- GitLab From a00100cacbe4345a0449b6f676aac5a203d736d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arno=20T=C3=B6ll?= Date: Sat, 5 May 2012 04:27:24 -0400 Subject: [PATCH 12/12] Spelling fix for templates/index/intro-maintainers.mako --- debexpo/templates/index/intro-maintainers.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debexpo/templates/index/intro-maintainers.mako b/debexpo/templates/index/intro-maintainers.mako index 22c82770..99cb74d6 100644 --- a/debexpo/templates/index/intro-maintainers.mako +++ b/debexpo/templates/index/intro-maintainers.mako @@ -13,7 +13,7 @@

2. File a WNPP bug

-

Work-Needing and Prospective Packages (WNPP) is our system of announcing your intent to markup packages being worked on. In particular it is a bug against the WNPP pseudo package (or use a nice frontend to browse WNPP bugs). If you want to package package something not currently available in Debian, the very first step should be to file an "Intent to package" (ITP) bug against WNPP. You may want to use the reportbug tool to achieve that by selecting "wnpp" as package to report a bug to.

+

Work-Needing and Prospective Packages (WNPP) is our system of announcing your intent to markup packages being worked on. In particular it is a bug against the WNPP pseudo package (or use a nice frontend to browse WNPP bugs). If you want to package something not currently available in Debian, the very first step should be to file an "Intent to package" (ITP) bug against WNPP. You may want to use the reportbug tool to achieve that by selecting "wnpp" as package to report a bug to.

3. Make the package

-- GitLab