Merge lp:~salgado/offspring/private-project-requires-owner into lp:~linaro-automation/offspring/private-builds

Proposed by Guilherme Salgado
Status: Merged
Approved by: James Tunnicliffe
Approved revision: no longer in the source branch.
Merged at revision: 72
Proposed branch: lp:~salgado/offspring/private-project-requires-owner
Merge into: lp:~linaro-automation/offspring/private-builds
Diff against target: 133 lines (+74/-5)
3 files modified
lib/offspring/web/queuemanager/forms.py (+9/-3)
lib/offspring/web/queuemanager/models.py (+2/-2)
lib/offspring/web/queuemanager/tests/test_views.py (+63/-0)
To merge this branch: bzr merge lp:~salgado/offspring/private-project-requires-owner
Reviewer Review Type Date Requested Status
James Tunnicliffe (community) Approve
Review via email: mp+80074@code.launchpad.net

Description of the change

This one ensures we can't have a private project without an owner, as that could render them completely inaccessible.

I've also removed the 'access_groups' field from +add/+edit project views because at this point we only need a single access group associated to a private project, and that's maintained via the +acl page. I also moved the declaration of the is_private and owner fields around in the model code so that they don't show up at the top of the form for creating/editing projects.

To post a comment you must log in.
Revision history for this message
James Tunnicliffe (dooferlad) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/offspring/web/queuemanager/forms.py'
2--- lib/offspring/web/queuemanager/forms.py 2011-10-10 14:53:58 +0000
3+++ lib/offspring/web/queuemanager/forms.py 2011-10-21 14:42:51 +0000
4@@ -4,7 +4,7 @@
5 from django.contrib.auth.models import User
6 from django.db import models
7 from django.forms import (
8- Form, ModelChoiceField, ModelForm, Textarea, TextInput)
9+ Form, ModelChoiceField, ModelForm, Textarea, TextInput, ValidationError)
10 from django.forms import fields
11
12 from offspring.web.queuemanager.models import (
13@@ -34,19 +34,25 @@
14 def clean_series(self):
15 return self.cleaned_data['series'].lower()
16
17+ def clean_owner(self):
18+ data = self.cleaned_data
19+ if data['_is_private'] and not data['owner']:
20+ raise ValidationError('A private project needs an owner')
21+ return self.cleaned_data['owner']
22+
23
24 class CreateProjectForm(ProjectBaseForm):
25 launchpad_project = ModelChoiceField(
26 LaunchpadProject.objects, widget=SelectWithAddNew, required=False)
27 class Meta(ProjectBaseForm.Meta):
28- exclude = ('priority', 'is_active', 'suite')
29+ exclude = ('priority', 'is_active', 'suite', 'access_groups')
30
31
32 class EditProjectForm(ProjectBaseForm):
33 launchpad_project = ModelChoiceField(
34 LaunchpadProject.objects, widget=SelectWithAddNew, required=False)
35 class Meta(ProjectBaseForm.Meta):
36- exclude = ('name', 'priority', 'is_active')
37+ exclude = ('name', 'priority', 'is_active', 'access_groups')
38
39
40 class AccessGroupMemberForm(Form):
41
42=== modified file 'lib/offspring/web/queuemanager/models.py'
43--- lib/offspring/web/queuemanager/models.py 2011-10-20 16:13:00 +0000
44+++ lib/offspring/web/queuemanager/models.py 2011-10-21 14:42:51 +0000
45@@ -203,12 +203,12 @@
46 # updating existing uses of Project.objects won't leak private projects.
47 objects = PublicOnlyObjectsManager()
48
49+ name = models.SlugField('codename', max_length=200, primary_key=True, unique=True)
50+ title = models.CharField('project title', max_length=30)
51 _is_private = models.BooleanField(default=False, db_column='is_private')
52 # We allow projects without an owner for backwards compatibility but
53 # there's a DB constraint to ensure private projects always have an owner.
54 owner = models.ForeignKey(User, blank=True, null=True)
55- name = models.SlugField('codename', max_length=200, primary_key=True, unique=True)
56- title = models.CharField('project title', max_length=30)
57 project_group = models.ForeignKey(ProjectGroup, blank=True, null=True)
58 priority = models.IntegerField(default=50)
59 is_active = models.BooleanField(default=True)
60
61=== modified file 'lib/offspring/web/queuemanager/tests/test_views.py'
62--- lib/offspring/web/queuemanager/tests/test_views.py 2011-10-20 16:13:00 +0000
63+++ lib/offspring/web/queuemanager/tests/test_views.py 2011-10-21 14:42:51 +0000
64@@ -107,6 +107,69 @@
65 response, self.get_expected_page_heading(project),
66 status_code=200, msg_prefix=response.content)
67
68+ def test_private_project_needs_owner(self):
69+ # A project can't be made private without an owner because that'd make
70+ # it completely inaccessible.
71+ project = factory.makeProject(is_private=False)
72+ user = factory.makeUser()
73+ self.grant_necessary_permission_to(user)
74+ self.assertTrue(
75+ self.client.login(username=user.username, password=user.username))
76+ data = {'status': [u'devel'],
77+ 'config_url': [u'http://example.com'],
78+ 'project_group': [u''],
79+ 'title': [u'test'],
80+ '_is_private': [u'on'],
81+ '_save': [u'Save'],
82+ 'series': [u'natty'],
83+ 'arch': [u'amd64']}
84+ response = self.client.post(
85+ reverse(self.view_path, args=[project.name]), data)
86+ self.assertContains(
87+ response, 'A private project needs an owner',
88+ status_code=200, msg_prefix=response.content)
89+
90+ # If we specify an owner, we'll be able to make that project private.
91+ data['owner'] = user.id
92+ response = self.client.post(
93+ reverse(self.view_path, args=[project.name]), data, follow=True)
94+ self.assertContains(
95+ response, 'This project is visible only to its owner',
96+ status_code=200, msg_prefix=response.content)
97+
98+
99+class ProjectCreateViewTests(TestCase):
100+ view_path = 'offspring.web.queuemanager.views.project_create'
101+
102+ def test_creating_a_private_project_needs_owner(self):
103+ # When creating a new private project, an owner for it must be
104+ # specified.
105+ user = factory.makeUser()
106+ grant_permission_to_user(user, 'add_project')
107+ self.assertTrue(
108+ self.client.login(username=user.username, password=user.username))
109+ data = {'status': [u'devel'],
110+ 'config_url': [u'http://example.com'],
111+ 'project_group': [u''],
112+ 'title': [u'test'],
113+ 'name': [u'test'],
114+ '_is_private': [u'on'],
115+ '_save': [u'Save'],
116+ 'series': [u'natty'],
117+ 'arch': [u'amd64']}
118+ response = self.client.post(reverse(self.view_path), data)
119+ self.assertContains(
120+ response, 'A private project needs an owner',
121+ status_code=200, msg_prefix=response.content)
122+
123+ # If we specify an owner, we'll be able to make that project private.
124+ data['owner'] = user.id
125+ response = self.client.post(
126+ reverse(self.view_path), data, follow=True)
127+ self.assertContains(
128+ response, 'This project is visible only to its owner',
129+ status_code=200, msg_prefix=response.content)
130+
131
132 class BuilderListTests(TestCase):
133 view_name = 'builder_list'

Subscribers

People subscribed via source and target branches