From 36309a674636e40d41c2e865950095ed230a5e49 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Oct 11 2017 17:52:53 +0000 Subject: Provide the sha256 and sha512 of the releases With this commit when a new release is uploading we create or update a `checksums` file in the release folder containing both the sha256 and the sha512 of the file(s) uploaded. Fixes https://pagure.io/pagure/issue/2622 Signed-off-by: Pierre-Yves Chibon --- diff --git a/pagure/lib/tasks.py b/pagure/lib/tasks.py index d9bf248..5fda99d 100644 --- a/pagure/lib/tasks.py +++ b/pagure/lib/tasks.py @@ -9,6 +9,7 @@ """ import gc +import hashlib import os import os.path import shutil @@ -682,3 +683,42 @@ def sync_pull_ref(name, namespace, user, requestid): session.remove() gc_clean() + + +@conn.task +def update_checksums_file(folder, filenames): + """ + """ + sha_file = os.path.join(folder, 'CHECKSUMS') + new_file = not os.path.exists(sha_file) + + if not new_file: + with open(sha_file) as stream: + row = stream.readline().strip() + if row != '# Generated and updated by pagure': + # This wasn't generated by pagure, don't touch it! + return + + algos = { + 'sha256': hashlib.sha256(), + 'sha512': hashlib.sha512(), + } + + for filename in filenames: + # for each files computes the different algorythm supported + with open(os.path.join(folder, filename), "rb") as stream: + while True: + buf = stream.read(2 * 2 ** 10) + if buf: + for hasher in algos.values(): + hasher.update(buf) + else: + break + + # Write them out to the output file + with open(sha_file, 'a') as stream: + if new_file: + stream.write('# Generated and updated by pagure\n') + for algo in algos: + stream.write('%s (%s) = %s\n' % ( + algo.upper(), filename, algos[algo].hexdigest())) diff --git a/pagure/templates/releases.html b/pagure/templates/releases.html index 8efb441..3cebcf5 100644 --- a/pagure/templates/releases.html +++ b/pagure/templates/releases.html @@ -31,6 +31,17 @@ release folder.

+{% if pagure_checksum %} +

+ The CHECKSUMS + file contains the checksums (sha256 and sha512) of the files uploaded to + pagure. +

+

+ To use this file, simply download it next to the tarball you downloaded and + run

sha512sum -c CHECKSUMS
+

+{% endif %} {% endif %}
diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index 55bb5ba..d79c370 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -913,12 +913,18 @@ def view_tags(repo, username=None, namespace=None): repo = flask.g.repo tags = pagure.lib.git.get_git_tags_objects(repo) + pagure_checksum = os.path.exists(os.path.join( + APP.config['UPLOAD_FOLDER_PATH'], + repo.fullname, + 'CHECKSUMS')) + return flask.render_template( 'releases.html', select='tags', username=username, repo=repo, tags=tags, + pagure_checksum=pagure_checksum, ) @@ -950,8 +956,10 @@ def new_release(repo, username=None, namespace=None): form = pagure.forms.UploadFileForm() if form.validate_on_submit(): + filenames = [] for filestream in flask.request.files.getlist('filestream'): filename = werkzeug.secure_filename(filestream.filename) + filenames.append(filename) try: folder = os.path.join( APP.config['UPLOAD_FOLDER_PATH'], @@ -970,6 +978,13 @@ def new_release(repo, username=None, namespace=None): except Exception as err: # pragma: no cover _log.exception(err) flask.flash('Upload failed', 'error') + + task = pagure.lib.tasks.update_checksums_file.delay( + folder=folder, filenames=filenames) + _log.info( + 'Updating checksums for %s of project %s in task: %s' % ( + filenames, repo.fullname, task.id)) + return flask.redirect(flask.url_for( 'view_tags', repo=repo.name, username=username, namespace=repo.namespace)) diff --git a/tests/test_pagure_flask_ui_repo.py b/tests/test_pagure_flask_ui_repo.py index d8794e8..64a00d4 100644 --- a/tests/test_pagure_flask_ui_repo.py +++ b/tests/test_pagure_flask_ui_repo.py @@ -18,6 +18,7 @@ import re import shutil import sys import tempfile +import time import os import pygit2 @@ -3972,6 +3973,9 @@ index 0000000..fb7093d csrf_token = output.data.split( 'name="csrf_token" type="hidden" value="')[1].split('">')[0] + upload_dir = os.path.join(self.path, 'releases') + self.assertEqual(os.listdir(upload_dir), []) + # Upload successful with open(img, mode='rb') as stream: data = {'filestream': stream, 'csrf_token': csrf_token} @@ -3984,6 +3988,40 @@ index 0000000..fb7093d 'uploaded\n ', output.data) self.assertIn('This project has not been tagged.', output.data) + self.assertEqual(os.listdir(upload_dir), ['test']) + folder = os.path.join(upload_dir, 'test') + checksum_file = os.path.join(folder, 'CHECKSUMS') + + # Wait for the worker to create the checksums file + cnt = 0 + while not os.path.exists(checksum_file): + print os.listdir(os.path.join(upload_dir, 'test')) + cnt += 1 + if cnt == 40: + raise ValueError( + 'The worker did not create the checksums file ' + 'in a timely manner') + time.sleep(0.5) + + self.assertEqual(len(os.listdir(folder)), 2) + + self.assertTrue(os.path.exists(checksum_file)) + + # Check the content of the checksums file + with open(checksum_file) as stream: + data = stream.readlines() + self.assertEqual(len(data), 3) + self.assertEqual(data[0], '# Generated and updated by pagure\n') + self.assertTrue(data[1].startswith('SHA256 (')) + self.assertTrue(data[1].endswith( + 'tests_placebo.png) = 8a06845923010b27bfd8e7e75acff' + '7badc40d1021b4994e01f5e11ca40bc3abe\n')) + self.assertTrue(data[2].startswith('SHA512 (')) + self.assertTrue(data[2].endswith( + 'tests_placebo.png) = 65a4458df0acb29dc3c5ad4a3620e' + '98841d1fcf3f8df358f5348fdeddd1a86706491ac6e416768e' + '9f218aae8147d6ac524a59d3eb91fb925fdcb5c489e55ccbb\n')) + # Try uploading the same file -- fails with open(img, mode='rb') as stream: data = {'filestream': stream, 'csrf_token': csrf_token}