1647
def setPrivate(self, private, who):
1650
We also record who made the change and when the change took
1654
def setPrivacyAndSecurityRelated(self, private, security_related, who):
1656
private_changed = False
1657
security_related_changed = False
1658
bug_before_modification = Snapshot(self, providing=providedBy(self))
1660
f_flag_str = 'disclosure.enhanced_private_bug_subscriptions.enabled'
1661
f_flag = bool(getFeatureFlag(f_flag_str))
1663
# Before we update the privacy or security_related status, we need to
1664
# reconcile the subscribers to avoid leaking private information.
1665
if (self.private != private
1666
or self.security_related != security_related):
1667
self.reconcileSubscribers(private, security_related, who)
1653
1669
if self.private != private:
1655
# Change indirect subscribers into direct subscribers
1656
# *before* setting private because
1657
# getIndirectSubscribers() behaves differently when
1658
# the bug is private.
1659
for person in self.getIndirectSubscribers():
1660
self.subscribe(person, who)
1661
subscribers_for_who = self.getSubscribersForPerson(who)
1662
if subscribers_for_who.is_empty():
1663
# We also add `who` as a subscriber, if they're not
1664
# already directly subscribed or part of a team
1665
# that's directly subscribed, so that they can
1666
# see the bug they've just marked private.
1667
self.subscribe(who, who)
1670
private_changed = True
1669
1671
self.private = private
1680
1682
for attachment in self.attachments_unpopulated:
1681
1683
attachment.libraryfile.restricted = private
1683
# Correct the heat for the bug immediately, so that we don't have
1684
# to wait for the next calculation job for the adjusted heat.
1686
return True # Changed.
1688
return False # Not changed.
1690
def setSecurityRelated(self, security_related):
1691
"""Setter for the `security_related` property."""
1692
1685
if self.security_related != security_related:
1686
security_related_changed = True
1693
1687
self.security_related = security_related
1689
if private_changed or security_related_changed:
1695
1690
# Correct the heat for the bug immediately, so that we don't have
1696
1691
# to wait for the next calculation job for the adjusted heat.
1697
1692
self.updateHeat()
1699
return True # Changed
1701
return False # Unchanged
1694
if private_changed or security_related_changed:
1697
changed_fields.append('private')
1698
if security_related_changed:
1699
changed_fields.append('security_related')
1700
if not f_flag and security_related:
1701
# The bug turned out to be security-related, subscribe the
1702
# security contact. We do it here only if the feature flag
1703
# is not set, otherwise it's done in
1704
# reconcileSubscribers().
1705
for pillar in self.affected_pillars:
1706
if pillar.security_contact is not None:
1707
self.subscribe(pillar.security_contact, who)
1708
notify(ObjectModifiedEvent(
1709
self, bug_before_modification, changed_fields, user=who))
1711
return private_changed, security_related_changed
1713
def setPrivate(self, private, who):
1716
We also record who made the change and when the change took
1719
return self.setPrivacyAndSecurityRelated(
1720
private, self.security_related, who)[0]
1722
def setSecurityRelated(self, security_related, who):
1723
"""Setter for the `security_related` property."""
1724
return self.setPrivacyAndSecurityRelated(
1725
self.private, security_related, who)[1]
1727
def getRequiredSubscribers(self, for_private, for_security_related, who):
1728
"""Return the mandatory subscribers for a bug with given attributes.
1730
When a bug is marked as private or security related, it is required
1731
that certain people be subscribed so they can access details about the
1733
security=true, private=true/false ->
1734
subscribers = the reporter + security contact for each task
1735
security=false, private=true ->
1736
subscribers = the reporter + bug supervisor for each task
1737
security=false, private=false ->
1740
If bug supervisor or security contact is unset, fallback to bugtask
1743
if not for_private and not for_security_related:
1746
result.add(self.owner)
1747
for bugtask in self.bugtasks:
1748
maintainer = bugtask.pillar.owner
1749
if for_security_related:
1750
result.add(bugtask.pillar.security_contact or maintainer)
1752
result.add(bugtask.pillar.bug_supervisor or maintainer)
1754
subscribers_for_who = self.getSubscribersForPerson(who)
1755
if subscribers_for_who.is_empty():
1759
def getAutoRemovedSubscribers(self, for_private, for_security_related):
1760
"""Return the to be removed subscribers for bug with given attributes.
1762
When a bug's privacy or security related attributes change, some
1763
existing subscribers may need to be automatically removed.
1766
auto removed subscribers = (bugtask security contacts)
1768
auto removed subscribers = (bugtask bug supervisors)
1771
bug_supervisors = []
1772
security_contacts = []
1773
for pillar in self.affected_pillars:
1774
if (self.security_related and not for_security_related
1775
and pillar.security_contact):
1776
security_contacts.append(pillar.security_contact)
1777
if (self.private and not for_private
1778
and pillar.bug_supervisor):
1779
bug_supervisors.append(pillar.bug_supervisor)
1780
return bug_supervisors, security_contacts
1782
def reconcileSubscribers(self, for_private, for_security_related, who):
1783
""" Ensure only appropriate people are subscribed to private bugs.
1785
When a bug is marked as either private = True or security_related =
1786
True, we need to ensure that only people who are authorised to know
1787
about the privileged contents of the bug remain directly subscribed
1789
1. Get the required subscribers depending on the bug status.
1790
2. Get the auto removed subscribers depending on the bug status.
1791
eg security contacts when a bug is updated to security related =
1793
3. Get the allowed subscribers = required subscribers
1795
4. Remove any current direct subscribers who are not allowed or are
1797
5. Add any subscribers who are required.
1799
current_direct_subscribers = (
1800
self.getSubscriptionInfo().direct_subscribers)
1801
required_subscribers = self.getRequiredSubscribers(
1802
for_private, for_security_related, who)
1803
removed_bug_supervisors, removed_security_contacts = (
1804
self.getAutoRemovedSubscribers(for_private, for_security_related))
1805
for subscriber in removed_bug_supervisors:
1806
recipients = BugNotificationRecipients()
1807
recipients.addBugSupervisor(subscriber)
1808
notification_text = ("This bug is no longer private so the bug "
1809
"supervisor was unsubscribed. They will no longer be "
1810
"notified of changes to this bug for privacy related "
1811
"reasons, but may receive notifications about this bug from "
1812
"other subscriptions.")
1814
subscriber, who, ignore_permissions=True,
1815
send_notification=True,
1816
notification_text=notification_text,
1817
recipients=recipients)
1818
for subscriber in removed_security_contacts:
1819
recipients = BugNotificationRecipients()
1820
recipients.addSecurityContact(subscriber)
1821
notification_text = ("This bug is no longer security related so "
1822
"the security contact was unsubscribed. They will no longer "
1823
"be notified of changes to this bug for security related "
1824
"reasons, but may receive notifications about this bug "
1825
"from other subscriptions.")
1827
subscriber, who, ignore_permissions=True,
1828
send_notification=True,
1829
notification_text=notification_text,
1830
recipients=recipients)
1832
# If this bug is for a project that is marked as having private bugs
1833
# by default, and the bug is private or security related, we will
1834
# unsubscribe any unauthorised direct subscribers.
1835
pillar = self.default_bugtask.pillar
1836
private_project = IProduct.providedBy(pillar) and pillar.private_bugs
1837
if private_project and (for_private or for_security_related):
1838
allowed_subscribers = set()
1839
allowed_subscribers.add(self.owner)
1840
for bugtask in self.bugtasks:
1841
allowed_subscribers.add(bugtask.owner)
1842
allowed_subscribers.add(bugtask.pillar.owner)
1843
allowed_subscribers.update(set(bugtask.pillar.drivers))
1844
allowed_subscribers = required_subscribers.union(
1845
allowed_subscribers)
1846
subscribers_to_remove = (
1847
current_direct_subscribers.difference(allowed_subscribers))
1848
for subscriber in subscribers_to_remove:
1849
self.unsubscribe(subscriber, who, ignore_permissions=True)
1851
subscribers_to_add = (
1852
required_subscribers.difference(current_direct_subscribers))
1853
for subscriber in subscribers_to_add:
1854
self.subscribe(subscriber, who)
1703
1856
def getBugTask(self, target):
1704
1857
"""See `IBug`."""