diff options
author | Ask Solem <askh@modwheel.net> | 2009-03-21 21:59:34 +0100 |
---|---|---|
committer | Ask Solem Hoel <askh@opera.com> | 2009-03-21 21:59:34 +0100 |
commit | 9a3a947bf307b6bc167d7a761dca2f17e84aacdb (patch) | |
tree | 351eda90269921f44b5e0105dd424ea1402476b5 | |
parent | 4335d5430fbcae6035f90495f1ce43d351e3927a (diff) | |
download | chishop-9a3a947bf307b6bc167d7a761dca2f17e84aacdb.tar.bz2 chishop-9a3a947bf307b6bc167d7a761dca2f17e84aacdb.tar.xz chishop-9a3a947bf307b6bc167d7a761dca2f17e84aacdb.zip |
Added authentication for register+upload, and projects now has owners.
-rw-r--r-- | djangopypi/forms.py | 42 | ||||
-rw-r--r-- | djangopypi/models.py | 2 | ||||
-rw-r--r-- | 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 | |||
35 | from djangopypi.models import Project, Classifier, Release | 35 | from djangopypi.models import Project, Classifier, Release |
36 | 36 | ||
37 | 37 | ||
38 | class PermissionDeniedError(Exception): | ||
39 | """The user did not have the priveliges to execute an action.""" | ||
40 | |||
41 | |||
38 | class ProjectRegisterForm(forms.Form): | 42 | class ProjectRegisterForm(forms.Form): |
39 | name = forms.CharField() | 43 | name = forms.CharField() |
40 | license = forms.CharField(required=False) | 44 | license = forms.CharField(required=False) |
@@ -48,12 +52,30 @@ class ProjectRegisterForm(forms.Form): | |||
48 | version = forms.CharField() | 52 | version = forms.CharField() |
49 | platform = forms.CharField(required=False) | 53 | platform = forms.CharField(required=False) |
50 | 54 | ||
51 | def save(self, classifiers, file=None): | 55 | PermissionDeniedError = PermissionDeniedError |
56 | |||
57 | def save(self, classifiers, user, file=None): | ||
52 | values = dict(self.cleaned_data) | 58 | values = dict(self.cleaned_data) |
53 | name = values.pop("name") | 59 | name = values["name"] |
54 | version = values.pop("version") | 60 | version = values.pop("version") |
55 | platform = values.pop("platform") | 61 | platform = values.pop("platform", "UNKNOWN") |
56 | project, c = Project.objects.get_or_create(name=name, defaults=values) | 62 | values["owner"] = user |
63 | |||
64 | try: | ||
65 | project = Project.objects.get(name=name) | ||
66 | except Project.DoesNotExist: | ||
67 | project = Project.objects.create(**values) | ||
68 | else: | ||
69 | # If the project already exists, | ||
70 | # be sure that the current user owns this object. | ||
71 | if project.owner != user: | ||
72 | raise self.PermissionDeniedError( | ||
73 | "%s doesn't own that project." % user.username) | ||
74 | [setattr(project, field_name, field_value) | ||
75 | for field_name, field_value in values.items()] | ||
76 | project.save() | ||
77 | |||
78 | |||
57 | for classifier in classifiers: | 79 | for classifier in classifiers: |
58 | project.classifiers.add( | 80 | project.classifiers.add( |
59 | Classifier.objects.get_or_create(name=classifier)[0]) | 81 | Classifier.objects.get_or_create(name=classifier)[0]) |
@@ -62,17 +84,17 @@ class ProjectRegisterForm(forms.Form): | |||
62 | # filename, however with .tar.gz files django does the "wrong" thing | 84 | # filename, however with .tar.gz files django does the "wrong" thing |
63 | # and saves it as project-0.1.2.tar_.gz. So remove it before | 85 | # and saves it as project-0.1.2.tar_.gz. So remove it before |
64 | # django sees anything. | 86 | # django sees anything. |
87 | |||
65 | if file: | 88 | if file: |
66 | try: | 89 | try: |
67 | previous_entry = Release.objects.get(version=version, | 90 | release = Release.objects.get(version=version, |
68 | platform=platform, project=project) | 91 | platform=platform, project=project) |
69 | if os.path.exists(previous_entry.distribution.path): | 92 | if os.path.exists(release.distribution.path): |
70 | os.remove(previous_entry.distribution.path) | 93 | os.remove(release.distribution.path) |
71 | previous_entry.delete() | 94 | release.delete() |
72 | except Release.DoesNotExist: | 95 | except (Release.DoesNotExist, ValueError): |
73 | pass | 96 | pass |
74 | 97 | ||
75 | |||
76 | release, created = Release.objects.get_or_create(version=version, | 98 | release, created = Release.objects.get_or_create(version=version, |
77 | platform=platform, | 99 | platform=platform, |
78 | project=project) | 100 | 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. | |||
33 | import os | 33 | import os |
34 | from django.db import models | 34 | from django.db import models |
35 | from django.utils.translation import ugettext_lazy as _ | 35 | from django.utils.translation import ugettext_lazy as _ |
36 | from django.contrib.auth.models import User | ||
36 | 37 | ||
37 | OS_NAMES = ( | 38 | OS_NAMES = ( |
38 | ("aix", "AIX"), | 39 | ("aix", "AIX"), |
@@ -85,6 +86,7 @@ class Project(models.Model): | |||
85 | description = models.TextField(blank=True) | 86 | description = models.TextField(blank=True) |
86 | author_email = models.CharField(max_length=255, blank=True) | 87 | author_email = models.CharField(max_length=255, blank=True) |
87 | classifiers = models.ManyToManyField(Classifier) | 88 | classifiers = models.ManyToManyField(Classifier) |
89 | owner = models.ForeignKey(User, related_name="projects") | ||
88 | 90 | ||
89 | class Meta: | 91 | class Meta: |
90 | verbose_name = _(u"project") | 92 | 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. | |||
31 | """ | 31 | """ |
32 | 32 | ||
33 | from django.http import Http404, HttpResponse, HttpResponseBadRequest | 33 | from django.http import Http404, HttpResponse, HttpResponseBadRequest |
34 | from django.http import QueryDict | 34 | from django.http import QueryDict, HttpResponseForbidden |
35 | from django.shortcuts import render_to_response | 35 | from django.shortcuts import render_to_response |
36 | from djangopypi.models import Project | 36 | from djangopypi.models import Project |
37 | from djangopypi.forms import ProjectRegisterForm | 37 | from djangopypi.forms import ProjectRegisterForm |
38 | from django.template import RequestContext | 38 | from django.template import RequestContext |
39 | from django.utils.datastructures import MultiValueDict | 39 | from django.utils.datastructures import MultiValueDict |
40 | from django.core.files.uploadedfile import SimpleUploadedFile | 40 | from django.core.files.uploadedfile import SimpleUploadedFile |
41 | from django.contrib.auth import authenticate, login | ||
41 | 42 | ||
42 | 43 | ||
43 | def parse_weird_post_data(data): | 44 | def parse_weird_post_data(data): |
@@ -71,15 +72,46 @@ def parse_weird_post_data(data): | |||
71 | return MultiValueDict(post_data), files | 72 | return MultiValueDict(post_data), files |
72 | 73 | ||
73 | 74 | ||
75 | def login_basic_auth(request): | ||
76 | authentication = request.META.get("HTTP_AUTHORIZATION") | ||
77 | if not authentication: | ||
78 | return | ||
79 | (authmeth, auth) = authentication.split(' ', 1) | ||
80 | if authmeth.lower() != "basic": | ||
81 | return | ||
82 | auth = auth.strip().decode("base64") | ||
83 | username, password = auth.split(":", 1) | ||
84 | return authenticate(username=username, password=password) | ||
85 | |||
86 | |||
87 | def authorization_required_response(): | ||
88 | response = HttpResponse("Authorization required", | ||
89 | mimetype="text/plain") | ||
90 | response['WWW-Authenticate'] = 'Basic realm="pypi"' | ||
91 | response.status_code = 401 | ||
92 | return response | ||
93 | |||
94 | |||
74 | def simple(request, template_name="djangopypi/simple.html"): | 95 | def simple(request, template_name="djangopypi/simple.html"): |
75 | if request.method == "POST": | 96 | if request.method == "POST": |
97 | user = login_basic_auth(request) | ||
98 | if not user: | ||
99 | return authorization_required_response() | ||
100 | login(request, user) | ||
101 | if not request.user.is_authenticated(): | ||
102 | return HttpResponseForbidden( | ||
103 | "Not logged in, or invalid username/password.") | ||
76 | post_data, files = parse_weird_post_data(request.raw_post_data) | 104 | post_data, files = parse_weird_post_data(request.raw_post_data) |
77 | action = post_data.get(":action") | 105 | action = post_data.get(":action") |
78 | classifiers = post_data.getlist("classifiers") | 106 | classifiers = post_data.getlist("classifiers") |
79 | register_form = ProjectRegisterForm(post_data.copy()) | 107 | register_form = ProjectRegisterForm(post_data.copy()) |
80 | if register_form.is_valid(): | 108 | if register_form.is_valid(): |
81 | return HttpResponse(register_form.save(classifiers, | 109 | try: |
82 | file=files.get("content"))) | 110 | register_form.save(classifiers, request.user, |
111 | file=files.get("content")) | ||
112 | except register_form.PermissionDeniedError: | ||
113 | return HttpResonseForbidden( | ||
114 | "That project is owned by someone else!") | ||
83 | return HttpResponse("Successfully registered.") | 115 | return HttpResponse("Successfully registered.") |
84 | return HttpResponse("ERRORS: %s" % register_form.errors) | 116 | return HttpResponse("ERRORS: %s" % register_form.errors) |
85 | 117 | ||