Merge lp:~adeuring/launchpad/bug-39674-flip-lfa-restricted-flag into lp:launchpad/db-devel

Proposed by Abel Deuring
Status: Merged
Approved by: Deryck Hodge
Approved revision: no longer in the source branch.
Merged at revision: 9616
Proposed branch: lp:~adeuring/launchpad/bug-39674-flip-lfa-restricted-flag
Merge into: lp:launchpad/db-devel
Diff against target: 185 lines (+68/-9)
5 files modified
database/schema/security.cfg (+1/-0)
lib/canonical/launchpad/tests/test_libraryfilealias_with_parent.py (+3/-3)
lib/lp/bugs/doc/bugattachments.txt (+42/-1)
lib/lp/bugs/interfaces/bug.py (+8/-2)
lib/lp/bugs/model/bug.py (+14/-3)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-39674-flip-lfa-restricted-flag
Reviewer Review Type Date Requested Status
Deryck Hodge (community) code Approve
Review via email: mp+31558@code.launchpad.net

Description of the change

This branch is another sequel to fix bug 39674: "Attachments of private bugreports are public".

When an attachment is adde to a private bug, the "restricted" flag of the related Librarian file is set. Similary, the "restricted" flag is changed when Bug.setPrivate() is called.

I removed the method IBug.linkAttachment(), which allows to link an existing LibraryFileAlias record to a bug attachment. Allowing to link existing LFAs to bug attachments could cause inconsistencies for LFA.restricted, if one LFA is linked to a private and to another public bug. (We had also a short discussion about this issue on the LP developers mailing list.)

Bug.linkAttachment() is at present only used in in bug.AddAttachment, so there was no need to explicity change other code. But I removed the section of doc/bugattachments.txt which describes this method. The "side tests", for example the patch flag, are covered elsewhere is that file.

test: ./bin/test -t bugattachments.txt

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/doc/bugattachments.txt
  lib/lp/bugs/interfaces/bug.py
  lib/lp/bugs/model/bug.py

./lib/lp/bugs/interfaces/bug.py
     375: E301 expected 1 blank line, found 2
    1064: E301 expected 1 blank line, found 2

Seems that the linter is confused by comments...

This branch is based on lp:~adeuring/launchpad/bug-39674-use-proxied-lfa which is reviewed but which is still in EC2. The diff against that branch:

=== modified file 'lib/lp/bugs/doc/bugattachments.txt'
--- lib/lp/bugs/doc/bugattachments.txt 2010-07-27 14:12:47 +0000
+++ lib/lp/bugs/doc/bugattachments.txt 2010-08-02 16:46:27 +0000
@@ -521,72 +521,25 @@
     True

-Linking existing LibraryFileAliases as attachments
---------------------------------------------------
-
-It's possible to link an existing LibraryFileAliases to a bug as an
-attachment by calling the bug's linkAttachment() method.
-
- >>> from canonical.launchpad.interfaces.librarian import (
- ... ILibraryFileAliasSet)
-
- >>> file_content = "Hello, world"
- >>> content_type = "text/plain"
- >>> file_alias = getUtility(ILibraryFileAliasSet).create(
- ... name='foobar', size=len(file_content),
- ... file=StringIO(file_content), contentType=content_type)
- >>> transaction.commit()
-
- >>> bug = factory.makeBug()
- >>> bug.linkAttachment(
- ... owner=bug.owner, file_alias=file_alias,
- ... comment="Some attachment")
- <BugAttachment ...>
-
- >>> bug.attachments.count()
- 1
- >>> attachment = bug.attachments[0]
- >>> print attachment.title
- foobar
-
-The attachment will have a type of BugAttachmentType.UNSPECIFIED, since
-we didn't specify that it was a patch.
-
- >>> print attachment.type.title
- Unspecified
-
-We can specify that the attachment is a patch and give it a more
-meaningful description.
-
- >>> file_alias = getUtility(ILibraryFileAliasSet).create(
- ... name='anotherfoobar', size=len(file_content),
- ... file=StringIO(file_content), contentType=content_type)
- >>> transaction.commit()
-
- >>> bug.linkAttachment(
- ... owner=bug.owner, file_alias=file_alias,
- ... comment="Some attachment", is_patch=True,
- ... description="An attachment of some sort")
- <BugAttachment ...>
-
- >>> bug.attachments.count()
- 2
- >>> attachment = bug.attachments[1]
- >>> print attachment.title
- An attachment of some sort
-
- >>> print attachment.type.title
- Patch
-
-
 Attachments without library files
 ---------------------------------

 It can happen that the LibraryFileContent record of a bug attachment is
 deleted, for example. because an admin deleted a privacy sensitive file.
 These attachments are not included in Bug.attachments. Our test bug has
-at present two attachments.
+two attachments.

+ >>> bug = factory.makeBug()
+ >>> file_content = "Hello, world"
+ >>> bug.addAttachment(
+ ... owner=bug.owner, data="Hello, world", filename="foobar",
+ ... comment="Some attachment")
+ <BugAttachment ...>
+ >>> bug.addAttachment(
+ ... owner=bug.owner, data="whatever", filename='anotherfoobar',
+ ... comment="Some attachment",
+ ... description="An attachment of some sort")
+ <BugAttachment ...>
     >>> [attachment.title for attachment in bug.attachments]
     [u'foobar', u'An attachment of some sort']

@@ -599,6 +552,42 @@
     [u'foobar']

+Adding bug attachments to private bugs
+--------------------------------------
+
+If an attachment is added to a private bug, the "restricted" flag of
+its Librarian file is set.
+
+ >>> private_bug_owner = factory.makePerson()
+ >>> login_person(private_bug_owner)
+ >>> private_bug = factory.makeBug(private=True, owner=private_bug_owner)
+ >>> private_attachment = private_bug.addAttachment(
+ ... owner=private_bug_owner, data="secret", filename="baz.txt",
+ ... comment="Some attachment")
+ >>> private_attachment.libraryfile.restricted
+ True
+
+But the "restricted" flag of Librarian files elonging to bug attachments
+of public bugs is not set.
+
+ >>> attachment.libraryfile.restricted
+ False
+
+If a private bug becomes public, the restricted flag of the related
+Librarian files are no longer set.
+
+ >>> changed = private_bug.setPrivate(False, private_bug.owner)
+ >>> private_attachment.libraryfile.restricted
+ False
+
+Similary, if a public bug becomes private, the "restricted" flag of
+its Librarian files are set.
+
+ >>> changed = bug.setPrivate(True, bug.owner)
+ >>> attachment.libraryfile.restricted
+ True
+
+
 Miscellaneous
 -------------

=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2010-07-22 12:17:41 +0000
+++ lib/lp/bugs/interfaces/bug.py 2010-08-02 16:51:31 +0000
@@ -135,6 +135,7 @@
         except NotFoundError:
             return None

+
 class IBugBecameQuestionEvent(Interface):
     """A bug became a question."""

@@ -522,18 +523,6 @@
         :is_patch: A boolean.
         """

- def linkAttachment(owner, file_alias, comment, is_patch=False,
- description=None):
- """Link an `ILibraryFileAlias` to this bug.
-
- :owner: An IPerson.
- :file_alias: The `ILibraryFileAlias` to link to this bug.
- :description: A brief description of the attachment.
- :comment: An IMessage or string.
- :filename: A string.
- :is_patch: A boolean.
- """
-
     def linkCVE(cve, user):
         """Ensure that this CVE is linked to this bug."""

@@ -716,7 +705,7 @@

         This may also cause the security contact to be subscribed
         if one is registered and if the bug is not private.
-
+
         Return True if a change is made, False otherwise.
         """

@@ -825,6 +814,7 @@
         Returns True or False.
         """

+
 class InvalidDuplicateValue(Exception):
     """A bug cannot be set as the duplicate of another."""
     webservice_error(417)

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-07-22 20:30:26 +0000
+++ lib/lp/bugs/model/bug.py 2010-08-02 16:50:45 +0000
@@ -694,7 +694,8 @@

         if recipients is not None:
             for subscriber in dupe_subscribers:
- recipients.addDupeSubscriber(subscriber, dupe_details[subscriber])
+ recipients.addDupeSubscriber(
+ subscriber, dupe_details[subscriber])

         return sorted(
             dupe_subscribers, key=operator.attrgetter("displayname"))
@@ -959,13 +960,26 @@

         filealias = getUtility(ILibraryFileAliasSet).create(
             name=filename, size=len(filecontent),
- file=StringIO(filecontent), contentType=content_type)
+ file=StringIO(filecontent), contentType=content_type,
+ restricted=self.private)

         return self.linkAttachment(
             owner, filealias, comment, is_patch, description)

     def linkAttachment(self, owner, file_alias, comment, is_patch=False,
                        description=None):
+ """Link an `ILibraryFileAlias` to this bug.
+
+ :owner: An IPerson.
+ :file_alias: The `ILibraryFileAlias` to link to this bug.
+ :description: A brief description of the attachment.
+ :comment: An IMessage or string.
+ :is_patch: A boolean.
+
+ This method should only be called by addAttachment(), otherwise
+ we may get inconsistent settings of bug.private and
+ file_alias.restricted.
+ """
         if is_patch:
             attach_type = BugAttachmentType.PATCH
         else:
@@ -1388,6 +1402,9 @@
                 self.who_made_private = None
                 self.date_made_private = None

+ for attachment in self.attachments:
+ attachment.libraryfile.restricted = private
+
             # Correct the heat for the bug immediately, so that we don't have
             # to wait for the next calculation job for the adjusted heat.
             self.updateHeat()
@@ -1941,4 +1958,3 @@
     def asDict(self):
         """Return the FileBugData instance as a dict."""
         return self.__dict__.copy()
-

To post a comment you must log in.
Revision history for this message
Deryck Hodge (deryck) wrote :

Looks good. As noted on IRC, there is one typo that I spotted:

"Librarian files elonging [...]" in bugattachment.txt.

I also suggested that we make it more obvious that linkAttachment is only used for addAttachment by renaming it to the pseudo-private form, _linkAttachment.

With these minor edits, consider this r=me.

Cheers,
deryck

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'database/schema/security.cfg'
2--- database/schema/security.cfg 2010-07-30 23:01:04 +0000
3+++ database/schema/security.cfg 2010-08-04 07:56:04 +0000
4@@ -1298,6 +1298,7 @@
5 public.answercontact = SELECT
6 public.archive = SELECT
7 public.archivearch = SELECT
8+public.bugattachment = SELECT
9 public.bugnotification = SELECT, INSERT, UPDATE
10 public.bugnotificationrecipient = SELECT, INSERT, UPDATE
11 public.bugsubscription = SELECT, INSERT
12
13=== modified file 'lib/canonical/launchpad/tests/test_libraryfilealias_with_parent.py'
14--- lib/canonical/launchpad/tests/test_libraryfilealias_with_parent.py 2010-07-06 17:08:00 +0000
15+++ lib/canonical/launchpad/tests/test_libraryfilealias_with_parent.py 2010-08-04 07:56:04 +0000
16@@ -36,10 +36,10 @@
17 # People who can edit the parent object can also edit
18 # LibraryFilasAlias instance.
19 login_person(self.bug_owner)
20+ self.assertTrue(self.lfa_with_parent.restricted)
21+ self.bug_attachment.title = 'foo'
22+ self.lfa_with_parent.restricted = False
23 self.assertFalse(self.lfa_with_parent.restricted)
24- self.bug_attachment.title = 'foo'
25- self.lfa_with_parent.restricted = True
26- self.assertTrue(self.lfa_with_parent.restricted)
27
28 def test_setRestricted_unauthorized_user(self):
29 # If a user cannot change properties of a bug attachment...
30
31=== modified file 'lib/lp/bugs/doc/bugattachments.txt'
32--- lib/lp/bugs/doc/bugattachments.txt 2010-07-27 14:12:47 +0000
33+++ lib/lp/bugs/doc/bugattachments.txt 2010-08-04 07:56:04 +0000
34@@ -525,7 +525,12 @@
35 --------------------------------------------------
36
37 It's possible to link an existing LibraryFileAliases to a bug as an
38-attachment by calling the bug's linkAttachment() method.
39+attachment by calling the bug's linkAttachment() method. Please note
40+that this method must not be used to reference the same LibraryFileAlias
41+record more than once. Doing this could cause inconsistencies between
42+LibraryFileAlias.restricted and Bug.private. See also the section
43+"Adding bug attachments to private bugs" below.
44+
45
46 >>> from canonical.launchpad.interfaces.librarian import (
47 ... ILibraryFileAliasSet)
48@@ -599,6 +604,42 @@
49 [u'foobar']
50
51
52+Adding bug attachments to private bugs
53+--------------------------------------
54+
55+If an attachment is added to a private bug, the "restricted" flag of
56+its Librarian file is set.
57+
58+ >>> private_bug_owner = factory.makePerson()
59+ >>> login_person(private_bug_owner)
60+ >>> private_bug = factory.makeBug(private=True, owner=private_bug_owner)
61+ >>> private_attachment = private_bug.addAttachment(
62+ ... owner=private_bug_owner, data="secret", filename="baz.txt",
63+ ... comment="Some attachment")
64+ >>> private_attachment.libraryfile.restricted
65+ True
66+
67+But the "restricted" flag of Librarian files belonging to bug attachments
68+of public bugs is not set.
69+
70+ >>> attachment.libraryfile.restricted
71+ False
72+
73+If a private bug becomes public, the restricted flag of the related
74+Librarian files are no longer set.
75+
76+ >>> changed = private_bug.setPrivate(False, private_bug.owner)
77+ >>> private_attachment.libraryfile.restricted
78+ False
79+
80+Similary, if a public bug becomes private, the "restricted" flag of
81+its Librarian files are set.
82+
83+ >>> changed = bug.setPrivate(True, bug.owner)
84+ >>> attachment.libraryfile.restricted
85+ True
86+
87+
88 Miscellaneous
89 -------------
90
91
92=== modified file 'lib/lp/bugs/interfaces/bug.py'
93--- lib/lp/bugs/interfaces/bug.py 2010-08-02 02:13:52 +0000
94+++ lib/lp/bugs/interfaces/bug.py 2010-08-04 07:56:04 +0000
95@@ -136,6 +136,7 @@
96 except NotFoundError:
97 return None
98
99+
100 class IBugBecameQuestionEvent(Interface):
101 """A bug became a question."""
102
103@@ -531,8 +532,12 @@
104 :file_alias: The `ILibraryFileAlias` to link to this bug.
105 :description: A brief description of the attachment.
106 :comment: An IMessage or string.
107- :filename: A string.
108 :is_patch: A boolean.
109+
110+ This method should only be called by addAttachment() and
111+ FileBugViewBase.submit_bug_action, otherwise
112+ we may get inconsistent settings of bug.private and
113+ file_alias.restricted.
114 """
115
116 def linkCVE(cve, user):
117@@ -717,7 +722,7 @@
118
119 This may also cause the security contact to be subscribed
120 if one is registered and if the bug is not private.
121-
122+
123 Return True if a change is made, False otherwise.
124 """
125
126@@ -826,6 +831,7 @@
127 Returns True or False.
128 """
129
130+
131 class InvalidDuplicateValue(Exception):
132 """A bug cannot be set as the duplicate of another."""
133 webservice_error(417)
134
135=== modified file 'lib/lp/bugs/model/bug.py'
136--- lib/lp/bugs/model/bug.py 2010-08-02 02:13:52 +0000
137+++ lib/lp/bugs/model/bug.py 2010-08-04 07:56:04 +0000
138@@ -694,7 +694,8 @@
139
140 if recipients is not None:
141 for subscriber in dupe_subscribers:
142- recipients.addDupeSubscriber(subscriber, dupe_details[subscriber])
143+ recipients.addDupeSubscriber(
144+ subscriber, dupe_details[subscriber])
145
146 return sorted(
147 dupe_subscribers, key=operator.attrgetter("displayname"))
148@@ -959,13 +960,21 @@
149
150 filealias = getUtility(ILibraryFileAliasSet).create(
151 name=filename, size=len(filecontent),
152- file=StringIO(filecontent), contentType=content_type)
153+ file=StringIO(filecontent), contentType=content_type,
154+ restricted=self.private)
155
156 return self.linkAttachment(
157 owner, filealias, comment, is_patch, description)
158
159 def linkAttachment(self, owner, file_alias, comment, is_patch=False,
160 description=None):
161+ """See `IBug`.
162+
163+ This method should only be called by addAttachment() and
164+ FileBugViewBase.submit_bug_action, otherwise
165+ we may get inconsistent settings of bug.private and
166+ file_alias.restricted.
167+ """
168 if is_patch:
169 attach_type = BugAttachmentType.PATCH
170 else:
171@@ -1388,6 +1397,9 @@
172 self.who_made_private = None
173 self.date_made_private = None
174
175+ for attachment in self.attachments:
176+ attachment.libraryfile.restricted = private
177+
178 # Correct the heat for the bug immediately, so that we don't have
179 # to wait for the next calculation job for the adjusted heat.
180 self.updateHeat()
181@@ -1941,4 +1953,3 @@
182 def asDict(self):
183 """Return the FileBugData instance as a dict."""
184 return self.__dict__.copy()
185-

Subscribers

People subscribed via source and target branches

to status/vote changes: