|
31 | 31 | from sentry.models.options.project_option import ProjectOption |
32 | 32 | from sentry.models.organization import Organization, OrganizationStatus |
33 | 33 | from sentry.models.organizationmapping import OrganizationMapping |
| 34 | +from sentry.models.organizationmemberreplayaccess import OrganizationMemberReplayAccess |
34 | 35 | from sentry.models.organizationslugreservation import OrganizationSlugReservation |
35 | 36 | from sentry.signals import project_created |
36 | 37 | from sentry.silo.safety import unguarded_write |
@@ -1476,6 +1477,202 @@ def test_enable_seer_coding_can_be_enabled(self) -> None: |
1476 | 1477 |
|
1477 | 1478 | assert self.organization.get_option("sentry:enable_seer_coding") is True |
1478 | 1479 |
|
| 1480 | + @with_feature("organizations:granular-replay-permissions") |
| 1481 | + def test_granular_replay_permissions_flag_set(self) -> None: |
| 1482 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1483 | + AuditLogEntry.objects.filter(organization_id=self.organization.id).delete() |
| 1484 | + |
| 1485 | + data = {"hasGranularReplayPermissions": True} |
| 1486 | + with outbox_runner(): |
| 1487 | + self.get_success_response(self.organization.slug, **data) |
| 1488 | + |
| 1489 | + option_value = OrganizationOption.objects.get( |
| 1490 | + organization=self.organization, key="sentry:granular-replay-permissions" |
| 1491 | + ) |
| 1492 | + assert option_value.value is True |
| 1493 | + |
| 1494 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1495 | + log = AuditLogEntry.objects.get(organization_id=self.organization.id) |
| 1496 | + assert "to True" in log.data["hasGranularReplayPermissions"] |
| 1497 | + |
| 1498 | + @with_feature("organizations:granular-replay-permissions") |
| 1499 | + def test_granular_replay_permissions_flag_unset(self) -> None: |
| 1500 | + self.organization.update_option("sentry:granular-replay-permissions", True) |
| 1501 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1502 | + AuditLogEntry.objects.filter(organization_id=self.organization.id).delete() |
| 1503 | + |
| 1504 | + data = {"hasGranularReplayPermissions": False} |
| 1505 | + with outbox_runner(): |
| 1506 | + self.get_success_response(self.organization.slug, **data) |
| 1507 | + |
| 1508 | + option_value = OrganizationOption.objects.get( |
| 1509 | + organization=self.organization, key="sentry:granular-replay-permissions" |
| 1510 | + ) |
| 1511 | + assert option_value.value is False |
| 1512 | + |
| 1513 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1514 | + log = AuditLogEntry.objects.get(organization_id=self.organization.id) |
| 1515 | + |
| 1516 | + assert "to False" in log.data["hasGranularReplayPermissions"] |
| 1517 | + |
| 1518 | + def test_granular_replay_permissions_flag_requires_feature(self) -> None: |
| 1519 | + data = {"hasGranularReplayPermissions": True} |
| 1520 | + response = self.get_error_response(self.organization.slug, **data, status_code=400) |
| 1521 | + assert "hasGranularReplayPermissions" in response.data |
| 1522 | + |
| 1523 | + @with_feature("organizations:granular-replay-permissions") |
| 1524 | + def test_granular_replay_permissions_flag_requires_admin_scope(self) -> None: |
| 1525 | + member_user = self.create_user() |
| 1526 | + self.create_member( |
| 1527 | + organization=self.organization, user=member_user, role="member", teams=[] |
| 1528 | + ) |
| 1529 | + self.login_as(member_user) |
| 1530 | + |
| 1531 | + data = {"hasGranularReplayPermissions": True} |
| 1532 | + response = self.get_error_response(self.organization.slug, **data, status_code=403) |
| 1533 | + assert response.status_code == 403 |
| 1534 | + |
| 1535 | + @with_feature("organizations:granular-replay-permissions") |
| 1536 | + def test_replay_access_members_add(self) -> None: |
| 1537 | + member1 = self.create_member( |
| 1538 | + organization=self.organization, user=self.create_user(), role="member" |
| 1539 | + ) |
| 1540 | + member2 = self.create_member( |
| 1541 | + organization=self.organization, user=self.create_user(), role="member" |
| 1542 | + ) |
| 1543 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1544 | + AuditLogEntry.objects.filter(organization_id=self.organization.id).delete() |
| 1545 | + |
| 1546 | + data = {"replayAccessMembers": [member1.id, member2.id]} |
| 1547 | + with outbox_runner(): |
| 1548 | + self.get_success_response(self.organization.slug, **data) |
| 1549 | + |
| 1550 | + access_members = list( |
| 1551 | + OrganizationMemberReplayAccess.objects.filter( |
| 1552 | + organization=self.organization |
| 1553 | + ).values_list("organizationmember_id", flat=True) |
| 1554 | + ) |
| 1555 | + assert set(access_members) == {member1.id, member2.id} |
| 1556 | + |
| 1557 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1558 | + log = AuditLogEntry.objects.get(organization_id=self.organization.id) |
| 1559 | + assert "added 2 member(s)" in log.data["replayAccessMembers"] |
| 1560 | + assert "total: 2 member(s)" in log.data["replayAccessMembers"] |
| 1561 | + |
| 1562 | + @with_feature("organizations:granular-replay-permissions") |
| 1563 | + def test_replay_access_members_remove(self) -> None: |
| 1564 | + member1 = self.create_member( |
| 1565 | + organization=self.organization, user=self.create_user(), role="member" |
| 1566 | + ) |
| 1567 | + member2 = self.create_member( |
| 1568 | + organization=self.organization, user=self.create_user(), role="member" |
| 1569 | + ) |
| 1570 | + OrganizationMemberReplayAccess.objects.create( |
| 1571 | + organization=self.organization, organizationmember=member1 |
| 1572 | + ) |
| 1573 | + OrganizationMemberReplayAccess.objects.create( |
| 1574 | + organization=self.organization, organizationmember=member2 |
| 1575 | + ) |
| 1576 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1577 | + AuditLogEntry.objects.filter(organization_id=self.organization.id).delete() |
| 1578 | + |
| 1579 | + data = {"replayAccessMembers": [member1.id]} |
| 1580 | + with outbox_runner(): |
| 1581 | + self.get_success_response(self.organization.slug, **data) |
| 1582 | + |
| 1583 | + access_members = list( |
| 1584 | + OrganizationMemberReplayAccess.objects.filter( |
| 1585 | + organization=self.organization |
| 1586 | + ).values_list("organizationmember_id", flat=True) |
| 1587 | + ) |
| 1588 | + assert access_members == [member1.id] |
| 1589 | + |
| 1590 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1591 | + log = AuditLogEntry.objects.get(organization_id=self.organization.id) |
| 1592 | + assert "removed 1 member(s)" in log.data["replayAccessMembers"] |
| 1593 | + assert "total: 1 member(s)" in log.data["replayAccessMembers"] |
| 1594 | + |
| 1595 | + @with_feature("organizations:granular-replay-permissions") |
| 1596 | + def test_replay_access_members_add_and_remove(self) -> None: |
| 1597 | + member1 = self.create_member( |
| 1598 | + organization=self.organization, user=self.create_user(), role="member" |
| 1599 | + ) |
| 1600 | + member2 = self.create_member( |
| 1601 | + organization=self.organization, user=self.create_user(), role="member" |
| 1602 | + ) |
| 1603 | + member3 = self.create_member( |
| 1604 | + organization=self.organization, user=self.create_user(), role="member" |
| 1605 | + ) |
| 1606 | + OrganizationMemberReplayAccess.objects.create( |
| 1607 | + organization=self.organization, organizationmember=member1 |
| 1608 | + ) |
| 1609 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1610 | + AuditLogEntry.objects.filter(organization_id=self.organization.id).delete() |
| 1611 | + |
| 1612 | + data = {"replayAccessMembers": [member2.id, member3.id]} |
| 1613 | + with outbox_runner(): |
| 1614 | + self.get_success_response(self.organization.slug, **data) |
| 1615 | + |
| 1616 | + access_members = set( |
| 1617 | + OrganizationMemberReplayAccess.objects.filter( |
| 1618 | + organization=self.organization |
| 1619 | + ).values_list("organizationmember_id", flat=True) |
| 1620 | + ) |
| 1621 | + assert access_members == {member2.id, member3.id} |
| 1622 | + |
| 1623 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1624 | + log = AuditLogEntry.objects.get(organization_id=self.organization.id) |
| 1625 | + assert "added 2 member(s)" in log.data["replayAccessMembers"] |
| 1626 | + assert "removed 1 member(s)" in log.data["replayAccessMembers"] |
| 1627 | + assert "total: 2 member(s)" in log.data["replayAccessMembers"] |
| 1628 | + |
| 1629 | + @with_feature("organizations:granular-replay-permissions") |
| 1630 | + def test_replay_access_members_clear_all(self) -> None: |
| 1631 | + member1 = self.create_member( |
| 1632 | + organization=self.organization, user=self.create_user(), role="member" |
| 1633 | + ) |
| 1634 | + OrganizationMemberReplayAccess.objects.create( |
| 1635 | + organization=self.organization, organizationmember=member1 |
| 1636 | + ) |
| 1637 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1638 | + AuditLogEntry.objects.filter(organization_id=self.organization.id).delete() |
| 1639 | + |
| 1640 | + data = {"replayAccessMembers": []} |
| 1641 | + with outbox_runner(): |
| 1642 | + self.get_success_response(self.organization.slug, **data) |
| 1643 | + |
| 1644 | + access_count = OrganizationMemberReplayAccess.objects.filter( |
| 1645 | + organization=self.organization |
| 1646 | + ).count() |
| 1647 | + assert access_count == 0 |
| 1648 | + |
| 1649 | + with assume_test_silo_mode_of(AuditLogEntry): |
| 1650 | + log = AuditLogEntry.objects.get(organization_id=self.organization.id) |
| 1651 | + assert "removed 1 member(s)" in log.data["replayAccessMembers"] |
| 1652 | + assert "total: 0 member(s)" in log.data["replayAccessMembers"] |
| 1653 | + |
| 1654 | + def test_replay_access_members_requires_feature(self) -> None: |
| 1655 | + member1 = self.create_member( |
| 1656 | + organization=self.organization, user=self.create_user(), role="member" |
| 1657 | + ) |
| 1658 | + data = {"replayAccessMembers": [member1.id]} |
| 1659 | + response = self.get_error_response(self.organization.slug, **data, status_code=400) |
| 1660 | + assert "hasGranularReplayPermissions" in response.data |
| 1661 | + |
| 1662 | + @with_feature("organizations:granular-replay-permissions") |
| 1663 | + def test_replay_access_members_requires_admin_scope(self) -> None: |
| 1664 | + member_user = self.create_user() |
| 1665 | + self.create_member( |
| 1666 | + organization=self.organization, user=member_user, role="member", teams=[] |
| 1667 | + ) |
| 1668 | + self.login_as(member_user) |
| 1669 | + |
| 1670 | + other_member = self.create_member( |
| 1671 | + organization=self.organization, user=self.create_user(), role="member" |
| 1672 | + ) |
| 1673 | + data = {"replayAccessMembers": [other_member.id]} |
| 1674 | + self.get_error_response(self.organization.slug, **data, status_code=403) |
| 1675 | + |
1479 | 1676 |
|
1480 | 1677 | class OrganizationDeleteTest(OrganizationDetailsTestBase): |
1481 | 1678 | method = "delete" |
|
0 commit comments