~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/bugs/model/bug.py

[r=wallyworld][no-qa] Rollback branch
        bug-subscribers-after-private-475775 due to a misinterpretation
        of the required functionality

Show diffs side-by-side

added added

removed removed

Lines of Context:
811
811
        self.updateHeat()
812
812
        return sub
813
813
 
814
 
    def unsubscribe(self, person, unsubscribed_by, **kwargs):
 
814
    def unsubscribe(self, person, unsubscribed_by):
815
815
        """See `IBug`."""
816
816
        # Drop cached subscription info.
817
817
        clear_property_cache(self)
821
821
        if person is None:
822
822
            person = unsubscribed_by
823
823
 
824
 
        ignore_permissions = kwargs.get('ignore_permissions', False)
825
824
        for sub in self.subscriptions:
826
825
            if sub.person.id == person.id:
827
 
                if (not ignore_permissions
828
 
                        and not sub.canBeUnsubscribedByUser(unsubscribed_by)):
 
826
                if not sub.canBeUnsubscribedByUser(unsubscribed_by):
829
827
                    raise UserCannotUnsubscribePerson(
830
828
                        '%s does not have permission to unsubscribe %s.' % (
831
829
                            unsubscribed_by.displayname,
1646
1644
 
1647
1645
        return bugtask
1648
1646
 
1649
 
    def setPrivacyAndSecurityRelated(self, private, security_related, who):
1650
 
        """ See `IBug`."""
1651
 
        private_changed = False
1652
 
        security_related_changed = False
1653
 
 
1654
 
        # Before we update the privacy or security_related status, we need to
1655
 
        # reconcile the subscribers to avoid leaking private information.
1656
 
        if (self.private != private
1657
 
                or self.security_related != security_related):
1658
 
            self.reconcileSubscribers(private, security_related, who)
1659
 
 
 
1647
    def setPrivate(self, private, who):
 
1648
        """See `IBug`.
 
1649
 
 
1650
        We also record who made the change and when the change took
 
1651
        place.
 
1652
        """
1660
1653
        if self.private != private:
1661
 
            private_changed = True
 
1654
            if 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)
 
1668
 
1662
1669
            self.private = private
1663
1670
 
1664
1671
            if private:
1673
1680
            for attachment in self.attachments_unpopulated:
1674
1681
                attachment.libraryfile.restricted = private
1675
1682
 
 
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.
 
1685
            self.updateHeat()
 
1686
            return True  # Changed.
 
1687
        else:
 
1688
            return False  # Not changed.
 
1689
 
 
1690
    def setSecurityRelated(self, security_related):
 
1691
        """Setter for the `security_related` property."""
1676
1692
        if self.security_related != security_related:
1677
 
            security_related_changed = True
1678
1693
            self.security_related = security_related
1679
1694
 
1680
 
        if private_changed or security_related_changed:
1681
1695
            # Correct the heat for the bug immediately, so that we don't have
1682
1696
            # to wait for the next calculation job for the adjusted heat.
1683
1697
            self.updateHeat()
1684
1698
 
1685
 
        return private_changed, security_related_changed
1686
 
 
1687
 
    def setPrivate(self, private, who):
1688
 
        """See `IBug`.
1689
 
 
1690
 
        We also record who made the change and when the change took
1691
 
        place.
1692
 
        """
1693
 
        return self.setPrivacyAndSecurityRelated(
1694
 
            private, self.security_related, who)[0]
1695
 
 
1696
 
    def setSecurityRelated(self, security_related, who):
1697
 
        """Setter for the `security_related` property."""
1698
 
        return self.setPrivacyAndSecurityRelated(
1699
 
            self.private, security_related, who)[1]
1700
 
 
1701
 
    def getRequiredSubscribers(self, for_private, for_security_related, who):
1702
 
        """Return the mandatory subscribers for a bug with given attributes.
1703
 
 
1704
 
        When a bug is marked as private or security related, it is required
1705
 
        that certain people be subscribed so they can access details about the
1706
 
        bug. The rules are:
1707
 
            security=true, private=true/false ->
1708
 
                subscribers = the reporter + security contact for each task
1709
 
            security=false, private=true ->
1710
 
                subscribers = the reporter + bug supervisor for each task
1711
 
            security=false, private=false ->
1712
 
                subscribers = ()
1713
 
 
1714
 
        If bug supervisor or security contact is unset, fallback to bugtask
1715
 
        reporter/owner.
1716
 
        """
1717
 
        if not for_private and not for_security_related:
1718
 
            return set()
1719
 
        result = set()
1720
 
        result.add(self.owner)
1721
 
        for bugtask in self.bugtasks:
1722
 
            maintainer = bugtask.pillar.owner
1723
 
            if for_security_related:
1724
 
                result.add(bugtask.pillar.security_contact or maintainer)
1725
 
            if for_private:
1726
 
                result.add(bugtask.pillar.bug_supervisor or maintainer)
1727
 
        if for_private:
1728
 
            subscribers_for_who = self.getSubscribersForPerson(who)
1729
 
            if subscribers_for_who.is_empty():
1730
 
                result.add(who)
1731
 
        return result
1732
 
 
1733
 
    def getAutoRemovedSubscribers(self, for_private, for_security_related):
1734
 
        """Return the to be removed subscribers for bug with given attributes.
1735
 
 
1736
 
        When a bug's privacy or security related attributes change, some
1737
 
        existing subscribers may need to be automatically removed.
1738
 
        The rules are:
1739
 
            security=false ->
1740
 
                auto removed subscribers = (bugtask security contacts)
1741
 
            privacy=false ->
1742
 
                auto removed subscribers = (bugtask bug supervisors)
1743
 
 
1744
 
        """
1745
 
        result = set()
1746
 
 
1747
 
        # There has been some discussion as to whether we really want to
1748
 
        # automatically unsubscribe the security contact if a bug is marked
1749
 
        # as no longer security related or the bug supervisor if a bug is no
1750
 
        # longer private.
1751
 
        # We want this behaviour for Launchpad but perhaps not Ubuntu.
1752
 
        # Until this can be clarified, we will not automatically unsubscribe
1753
 
        # anyone.
1754
 
 
1755
 
#        for bugtask in self.bugtasks:
1756
 
#            if not for_security_related and bugtask.pillar.security_contact:
1757
 
#                result.add(bugtask.pillar.security_contact)
1758
 
#            if not for_private and bugtask.pillar.bug_supervisor:
1759
 
#                result.add(bugtask.pillar.bug_supervisor)
1760
 
        return result
1761
 
 
1762
 
    def reconcileSubscribers(self, for_private, for_security_related, who):
1763
 
        """ Ensure only appropriate people are subscribed to private bugs.
1764
 
 
1765
 
        When a bug is marked as either private = True or security_related =
1766
 
        True, we need to ensure that only people who are authorised to know
1767
 
        about the privileged contents of the bug remain directly subscribed
1768
 
        to it. So we:
1769
 
          1. Get the required subscribers depending on the bug status.
1770
 
          2. Get the auto removed subscribers depending on the bug status.
1771
 
             eg security contacts when a bug is updated to security related =
1772
 
             false.
1773
 
          3. Get the allowed subscribers = required subscribers
1774
 
                                            + bugtask owners
1775
 
          4. Remove any current direct subscribers who are not allowed or are
1776
 
             to be auto removed.
1777
 
          5. Add any subscribers who are required.
1778
 
        """
1779
 
        current_direct_subscribers = (
1780
 
            self.getSubscriptionInfo().direct_subscribers)
1781
 
        required_subscribers = self.getRequiredSubscribers(
1782
 
            for_private, for_security_related, who)
1783
 
        auto_removed_subscribers = self.getAutoRemovedSubscribers(
1784
 
            for_private, for_security_related)
1785
 
        allowed_subscribers = set()
1786
 
        allowed_subscribers.add(self.owner)
1787
 
        for bugtask in self.bugtasks:
1788
 
            allowed_subscribers.add(bugtask.owner)
1789
 
            allowed_subscribers.add(bugtask.pillar.owner)
1790
 
            allowed_subscribers.update(set(bugtask.pillar.drivers))
1791
 
        allowed_subscribers = required_subscribers.union(allowed_subscribers)
1792
 
 
1793
 
        subscribers_to_remove = auto_removed_subscribers
1794
 
        if for_private or for_security_related:
1795
 
            subscribers_to_remove = auto_removed_subscribers.union(
1796
 
                current_direct_subscribers.difference(allowed_subscribers))
1797
 
        for subscriber in subscribers_to_remove:
1798
 
            self.unsubscribe(subscriber, who, ignore_permissions=True)
1799
 
 
1800
 
        subscribers_to_add = (
1801
 
            required_subscribers.difference(current_direct_subscribers))
1802
 
        for subscriber in subscribers_to_add:
1803
 
            self.subscribe(subscriber, who)
 
1699
            return True  # Changed
 
1700
        else:
 
1701
            return False  # Unchanged
1804
1702
 
1805
1703
    def getBugTask(self, target):
1806
1704
        """See `IBug`."""