1648
def setPrivate(self, private, who):
1651
We also record who made the change and when the change took
1652
def setPrivacyAndSecurityRelated(self, private, security_related, who):
1654
private_changed = False
1655
security_related_changed = False
1656
bug_before_modification = Snapshot(self, providing=providedBy(self))
1658
# Before we update the privacy or security_related status, we need to
1659
# reconcile the subscribers to avoid leaking private information.
1660
if (self.private != private
1661
or self.security_related != security_related):
1662
self.reconcileSubscribers(private, security_related, who)
1654
1664
if self.private != private:
1656
# Change indirect subscribers into direct subscribers
1657
# *before* setting private because
1658
# getIndirectSubscribers() behaves differently when
1659
# the bug is private.
1660
for person in self.getIndirectSubscribers():
1661
self.subscribe(person, who)
1662
subscribers_for_who = self.getSubscribersForPerson(who)
1663
if subscribers_for_who.is_empty():
1664
# We also add `who` as a subscriber, if they're not
1665
# already directly subscribed or part of a team
1666
# that's directly subscribed, so that they can
1667
# see the bug they've just marked private.
1668
self.subscribe(who, who)
1665
private_changed = True
1670
1666
self.private = private
1681
1677
for attachment in self.attachments_unpopulated:
1682
1678
attachment.libraryfile.restricted = private
1684
# Correct the heat for the bug immediately, so that we don't have
1685
# to wait for the next calculation job for the adjusted heat.
1687
return True # Changed.
1689
return False # Not changed.
1691
def setSecurityRelated(self, security_related):
1692
"""Setter for the `security_related` property."""
1693
1680
if self.security_related != security_related:
1681
security_related_changed = True
1694
1682
self.security_related = security_related
1684
if private_changed or security_related_changed:
1696
1685
# Correct the heat for the bug immediately, so that we don't have
1697
1686
# to wait for the next calculation job for the adjusted heat.
1698
1687
self.updateHeat()
1700
return True # Changed
1702
return False # Unchanged
1689
if private_changed or security_related_changed:
1692
changed_fields.append('private')
1693
if security_related_changed:
1694
changed_fields.append('security_related')
1695
notify(ObjectModifiedEvent(
1696
self, bug_before_modification, changed_fields, user=who))
1698
return private_changed, security_related_changed
1700
def setPrivate(self, private, who):
1703
We also record who made the change and when the change took
1706
return self.setPrivacyAndSecurityRelated(
1707
private, self.security_related, who)[0]
1709
def setSecurityRelated(self, security_related, who):
1710
"""Setter for the `security_related` property."""
1711
return self.setPrivacyAndSecurityRelated(
1712
self.private, security_related, who)[1]
1714
def getRequiredSubscribers(self, for_private, for_security_related, who):
1715
"""Return the mandatory subscribers for a bug with given attributes.
1717
When a bug is marked as private or security related, it is required
1718
that certain people be subscribed so they can access details about the
1720
security=true, private=true/false ->
1721
subscribers = the reporter + security contact for each task
1722
security=false, private=true ->
1723
subscribers = the reporter + bug supervisor for each task
1724
security=false, private=false ->
1727
If bug supervisor or security contact is unset, fallback to bugtask
1730
if not for_private and not for_security_related:
1733
result.add(self.owner)
1734
for bugtask in self.bugtasks:
1735
maintainer = bugtask.pillar.owner
1736
if for_security_related:
1737
result.add(bugtask.pillar.security_contact or maintainer)
1739
result.add(bugtask.pillar.bug_supervisor or maintainer)
1741
subscribers_for_who = self.getSubscribersForPerson(who)
1742
if subscribers_for_who.is_empty():
1746
def getAutoRemovedSubscribers(self, for_private, for_security_related):
1747
"""Return the to be removed subscribers for bug with given attributes.
1749
When a bug's privacy or security related attributes change, some
1750
existing subscribers may need to be automatically removed.
1753
auto removed subscribers = (bugtask security contacts)
1755
auto removed subscribers = (bugtask bug supervisors)
1758
bug_supervisors = []
1759
security_contacts = []
1760
for pillar in self.affected_pillars:
1761
if (self.security_related and not for_security_related
1762
and pillar.security_contact):
1763
security_contacts.append(pillar.security_contact)
1764
if (self.private and not for_private
1765
and pillar.bug_supervisor):
1766
bug_supervisors.append(pillar.bug_supervisor)
1767
return bug_supervisors, security_contacts
1769
def reconcileSubscribers(self, for_private, for_security_related, who):
1770
""" Ensure only appropriate people are subscribed to private bugs.
1772
When a bug is marked as either private = True or security_related =
1773
True, we need to ensure that only people who are authorised to know
1774
about the privileged contents of the bug remain directly subscribed
1776
1. Get the required subscribers depending on the bug status.
1777
2. Get the auto removed subscribers depending on the bug status.
1778
eg security contacts when a bug is updated to security related =
1780
3. Get the allowed subscribers = required subscribers
1782
4. Remove any current direct subscribers who are not allowed or are
1784
5. Add any subscribers who are required.
1786
current_direct_subscribers = (
1787
self.getSubscriptionInfo().direct_subscribers)
1788
required_subscribers = self.getRequiredSubscribers(
1789
for_private, for_security_related, who)
1790
removed_bug_supervisors, removed_security_contacts = (
1791
self.getAutoRemovedSubscribers(for_private, for_security_related))
1792
for subscriber in removed_bug_supervisors:
1793
recipients = BugNotificationRecipients()
1794
recipients.addBugSupervisor(subscriber)
1795
notification_text = ("This bug is no longer private so the bug "
1796
"supervisor was unsubscribed. You will no longer be notified "
1797
"of changes to this bug for privacy related reasons, but you "
1798
"may receive notifications about this bug from other "
1801
subscriber, who, ignore_permissions=True,
1802
send_notification=True,
1803
notification_text=notification_text,
1804
recipients=recipients)
1805
for subscriber in removed_security_contacts:
1806
recipients = BugNotificationRecipients()
1807
recipients.addSecurityContact(subscriber)
1808
notification_text = ("This bug is no longer security related so "
1809
"the security contact was unsubscribed. You will no longer "
1810
"be notified of changes to this bug for privacy related "
1811
"reasons, but you may receive notifications about this bug "
1812
"from other subscriptions.")
1814
subscriber, who, ignore_permissions=True,
1815
send_notification=True,
1816
notification_text=notification_text,
1817
recipients=recipients)
1819
# If this bug is for a project that is marked as having private bugs
1820
# by default, and the bug is private or security related, we will
1821
# unsubscribe any unauthorised direct subscribers.
1822
pillar = self.default_bugtask.pillar
1823
private_project = IProduct.providedBy(pillar) and pillar.private_bugs
1824
if private_project and (for_private or for_security_related):
1825
allowed_subscribers = set()
1826
allowed_subscribers.add(self.owner)
1827
for bugtask in self.bugtasks:
1828
allowed_subscribers.add(bugtask.owner)
1829
allowed_subscribers.add(bugtask.pillar.owner)
1830
allowed_subscribers.update(set(bugtask.pillar.drivers))
1831
allowed_subscribers = required_subscribers.union(
1832
allowed_subscribers)
1833
subscribers_to_remove = (
1834
current_direct_subscribers.difference(allowed_subscribers))
1835
for subscriber in subscribers_to_remove:
1836
self.unsubscribe(subscriber, who, ignore_permissions=True)
1838
subscribers_to_add = (
1839
required_subscribers.difference(current_direct_subscribers))
1840
for subscriber in subscribers_to_add:
1841
self.subscribe(subscriber, who)
1704
1843
def getBugTask(self, target):
1705
1844
"""See `IBug`."""