From 9a3a947bf307b6bc167d7a761dca2f17e84aacdb Mon Sep 17 00:00:00 2001 From: Ask Solem Date: Sat, 21 Mar 2009 21:59:34 +0100 Subject: Added authentication for register+upload, and projects now has owners. --- djangopypi/forms.py | 42 ++++++++++++++++++++++++++++++++---------- djangopypi/models.py | 2 ++ djangopypi/views.py | 38 +++++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/djangopypi/forms.py b/djangopypi/forms.py index 3217e7c..90682e2 100644 --- a/djangopypi/forms.py +++ b/djangopypi/forms.py @@ -35,6 +35,10 @@ from django import forms from djangopypi.models import Project, Classifier, Release +class PermissionDeniedError(Exception): + """The user did not have the priveliges to execute an action.""" + + class ProjectRegisterForm(forms.Form): name = forms.CharField() license = forms.CharField(required=False) @@ -48,12 +52,30 @@ class ProjectRegisterForm(forms.Form): version = forms.CharField() platform = forms.CharField(required=False) - def save(self, classifiers, file=None): + PermissionDeniedError = PermissionDeniedError + + def save(self, classifiers, user, file=None): values = dict(self.cleaned_data) - name = values.pop("name") + name = values["name"] version = values.pop("version") - platform = values.pop("platform") - project, c = Project.objects.get_or_create(name=name, defaults=values) + platform = values.pop("platform", "UNKNOWN") + values["owner"] = user + + try: + project = Project.objects.get(name=name) + except Project.DoesNotExist: + project = Project.objects.create(**values) + else: + # If the project already exists, + # be sure that the current user owns this object. + if project.owner != user: + raise self.PermissionDeniedError( + "%s doesn't own that project." % user.username) + [setattr(project, field_name, field_value) + for field_name, field_value in values.items()] + project.save() + + for classifier in classifiers: project.classifiers.add( Classifier.objects.get_or_create(name=classifier)[0]) @@ -62,17 +84,17 @@ class ProjectRegisterForm(forms.Form): # filename, however with .tar.gz files django does the "wrong" thing # and saves it as project-0.1.2.tar_.gz. So remove it before # django sees anything. + if file: try: - previous_entry = Release.objects.get(version=version, + release = Release.objects.get(version=version, platform=platform, project=project) - if os.path.exists(previous_entry.distribution.path): - os.remove(previous_entry.distribution.path) - previous_entry.delete() - except Release.DoesNotExist: + if os.path.exists(release.distribution.path): + os.remove(release.distribution.path) + release.delete() + except (Release.DoesNotExist, ValueError): pass - release, created = Release.objects.get_or_create(version=version, platform=platform, project=project) diff --git a/djangopypi/models.py b/djangopypi/models.py index e94c19e..aa62703 100644 --- a/djangopypi/models.py +++ b/djangopypi/models.py @@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. import os from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User OS_NAMES = ( ("aix", "AIX"), @@ -85,6 +86,7 @@ class Project(models.Model): description = models.TextField(blank=True) author_email = models.CharField(max_length=255, blank=True) classifiers = models.ManyToManyField(Classifier) + owner = models.ForeignKey(User, related_name="projects") class Meta: verbose_name = _(u"project") diff --git a/djangopypi/views.py b/djangopypi/views.py index b1323fb..368aea4 100644 --- a/djangopypi/views.py +++ b/djangopypi/views.py @@ -31,13 +31,14 @@ POSSIBILITY OF SUCH DAMAGE. """ from django.http import Http404, HttpResponse, HttpResponseBadRequest -from django.http import QueryDict +from django.http import QueryDict, HttpResponseForbidden from django.shortcuts import render_to_response from djangopypi.models import Project from djangopypi.forms import ProjectRegisterForm from django.template import RequestContext from django.utils.datastructures import MultiValueDict from django.core.files.uploadedfile import SimpleUploadedFile +from django.contrib.auth import authenticate, login def parse_weird_post_data(data): @@ -71,15 +72,46 @@ def parse_weird_post_data(data): return MultiValueDict(post_data), files +def login_basic_auth(request): + authentication = request.META.get("HTTP_AUTHORIZATION") + if not authentication: + return + (authmeth, auth) = authentication.split(' ', 1) + if authmeth.lower() != "basic": + return + auth = auth.strip().decode("base64") + username, password = auth.split(":", 1) + return authenticate(username=username, password=password) + + +def authorization_required_response(): + response = HttpResponse("Authorization required", + mimetype="text/plain") + response['WWW-Authenticate'] = 'Basic realm="pypi"' + response.status_code = 401 + return response + + def simple(request, template_name="djangopypi/simple.html"): if request.method == "POST": + user = login_basic_auth(request) + if not user: + return authorization_required_response() + login(request, user) + if not request.user.is_authenticated(): + return HttpResponseForbidden( + "Not logged in, or invalid username/password.") post_data, files = parse_weird_post_data(request.raw_post_data) action = post_data.get(":action") classifiers = post_data.getlist("classifiers") register_form = ProjectRegisterForm(post_data.copy()) if register_form.is_valid(): - return HttpResponse(register_form.save(classifiers, - file=files.get("content"))) + try: + register_form.save(classifiers, request.user, + file=files.get("content")) + except register_form.PermissionDeniedError: + return HttpResonseForbidden( + "That project is owned by someone else!") return HttpResponse("Successfully registered.") return HttpResponse("ERRORS: %s" % register_form.errors) -- cgit v1.2.3