mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
Links to the audit log view via the remote support view in the remote server information section after the user counts.
385 lines
15 KiB
Python
385 lines
15 KiB
Python
import uuid
|
|
from datetime import datetime, timedelta, timezone
|
|
from unittest import mock
|
|
|
|
from django.utils.timezone import now as timezone_now
|
|
|
|
from corporate.lib.activity import get_remote_server_audit_logs
|
|
from corporate.lib.stripe import add_months
|
|
from corporate.models import Customer, CustomerPlan, LicenseLedger
|
|
from zerver.lib.test_classes import ZulipTestCase
|
|
from zerver.models import Client, UserActivity, UserProfile
|
|
from zerver.models.realm_audit_logs import AuditLogEventType
|
|
from zilencer.models import (
|
|
RemoteRealm,
|
|
RemoteRealmAuditLog,
|
|
RemoteZulipServer,
|
|
RemoteZulipServerAuditLog,
|
|
get_remote_customer_user_count,
|
|
get_remote_server_guest_and_non_guest_count,
|
|
)
|
|
|
|
event_time = timezone_now() - timedelta(days=3)
|
|
data_list = [
|
|
{
|
|
"server_id": 1,
|
|
"realm_id": 1,
|
|
"event_type": AuditLogEventType.USER_CREATED,
|
|
"event_time": event_time,
|
|
"extra_data": {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 10,
|
|
UserProfile.ROLE_REALM_OWNER: 10,
|
|
UserProfile.ROLE_MODERATOR: 10,
|
|
UserProfile.ROLE_MEMBER: 10,
|
|
UserProfile.ROLE_GUEST: 10,
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"server_id": 1,
|
|
"realm_id": 1,
|
|
"event_type": AuditLogEventType.USER_ROLE_CHANGED,
|
|
"event_time": event_time,
|
|
"extra_data": {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 20,
|
|
UserProfile.ROLE_REALM_OWNER: 0,
|
|
UserProfile.ROLE_MODERATOR: 0,
|
|
UserProfile.ROLE_MEMBER: 20,
|
|
UserProfile.ROLE_GUEST: 10,
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"server_id": 1,
|
|
"realm_id": 2,
|
|
"event_type": AuditLogEventType.USER_CREATED,
|
|
"event_time": event_time,
|
|
"extra_data": {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 10,
|
|
UserProfile.ROLE_REALM_OWNER: 10,
|
|
UserProfile.ROLE_MODERATOR: 0,
|
|
UserProfile.ROLE_MEMBER: 10,
|
|
UserProfile.ROLE_GUEST: 5,
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"server_id": 1,
|
|
"realm_id": 2,
|
|
"event_type": AuditLogEventType.USER_CREATED,
|
|
"event_time": event_time,
|
|
"extra_data": {},
|
|
},
|
|
{
|
|
"server_id": 1,
|
|
"realm_id": 3,
|
|
"event_type": AuditLogEventType.USER_CREATED,
|
|
"event_time": event_time,
|
|
"extra_data": {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 1,
|
|
UserProfile.ROLE_REALM_OWNER: 1,
|
|
UserProfile.ROLE_MODERATOR: 1,
|
|
UserProfile.ROLE_MEMBER: 1,
|
|
UserProfile.ROLE_GUEST: 1,
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
"server_id": 1,
|
|
"realm_id": 3,
|
|
"event_type": AuditLogEventType.USER_DEACTIVATED,
|
|
"event_time": event_time + timedelta(seconds=1),
|
|
"extra_data": {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 1,
|
|
UserProfile.ROLE_REALM_OWNER: 1,
|
|
UserProfile.ROLE_MODERATOR: 1,
|
|
UserProfile.ROLE_MEMBER: 0,
|
|
UserProfile.ROLE_GUEST: 1,
|
|
}
|
|
}
|
|
},
|
|
},
|
|
]
|
|
|
|
|
|
class ActivityTest(ZulipTestCase):
|
|
@mock.patch("stripe.Customer.list", return_value=[])
|
|
def test_activity(self, unused_mock: mock.Mock) -> None:
|
|
self.login("hamlet")
|
|
client, _ = Client.objects.get_or_create(name="website")
|
|
query = "/json/messages/flags"
|
|
last_visit = timezone_now()
|
|
count = 150
|
|
for activity_user_profile in UserProfile.objects.all():
|
|
UserActivity.objects.get_or_create(
|
|
user_profile=activity_user_profile,
|
|
client=client,
|
|
query=query,
|
|
count=count,
|
|
last_visit=last_visit,
|
|
)
|
|
|
|
# Fails when not staff
|
|
result = self.client_get("/activity")
|
|
self.assertEqual(result.status_code, 302)
|
|
|
|
user_profile = self.example_user("hamlet")
|
|
user_profile.is_staff = True
|
|
user_profile.save(update_fields=["is_staff"])
|
|
|
|
with self.assert_database_query_count(11):
|
|
result = self.client_get("/activity")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
# Add data for remote activity page
|
|
remote_realm = RemoteRealm.objects.get(name="Lear & Co.")
|
|
customer = Customer.objects.create(remote_realm=remote_realm)
|
|
plan = CustomerPlan.objects.create(
|
|
customer=customer,
|
|
billing_cycle_anchor=timezone_now(),
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
|
tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS,
|
|
price_per_license=8000,
|
|
next_invoice_date=add_months(timezone_now(), 12),
|
|
)
|
|
LicenseLedger.objects.create(
|
|
licenses=10,
|
|
licenses_at_next_renewal=10,
|
|
event_time=timezone_now(),
|
|
is_renewal=True,
|
|
plan=plan,
|
|
)
|
|
server = RemoteZulipServer.objects.create(
|
|
uuid=str(uuid.uuid4()),
|
|
api_key="magic_secret_api_key",
|
|
hostname="demo.example.com",
|
|
contact_email="email@example.com",
|
|
)
|
|
RemoteZulipServerAuditLog.objects.create(
|
|
event_type=AuditLogEventType.REMOTE_SERVER_CREATED,
|
|
server=server,
|
|
event_time=server.last_updated,
|
|
)
|
|
extra_data = {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 1,
|
|
UserProfile.ROLE_REALM_OWNER: 1,
|
|
UserProfile.ROLE_MODERATOR: 1,
|
|
UserProfile.ROLE_MEMBER: 1,
|
|
UserProfile.ROLE_GUEST: 1,
|
|
}
|
|
}
|
|
}
|
|
RemoteRealmAuditLog.objects.create(
|
|
server=server,
|
|
realm_id=10,
|
|
event_type=AuditLogEventType.USER_CREATED,
|
|
event_time=timezone_now() - timedelta(days=1),
|
|
extra_data=extra_data,
|
|
)
|
|
with self.assert_database_query_count(10):
|
|
result = self.client_get("/activity/remote")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
with self.assert_database_query_count(5):
|
|
result = self.client_get("/activity/integrations")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
with self.assert_database_query_count(7):
|
|
result = self.client_get("/realm_activity/zulip/")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
iago = self.example_user("iago")
|
|
with self.assert_database_query_count(6):
|
|
result = self.client_get(f"/user_activity/{iago.id}/")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
with self.assert_database_query_count(8):
|
|
result = self.client_get(f"/activity/plan_ledger/{plan.id}/")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
with self.assert_database_query_count(7):
|
|
result = self.client_get(f"/activity/remote/logs/server/{server.uuid}/")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
def test_get_remote_server_guest_and_non_guest_count(self) -> None:
|
|
RemoteRealmAuditLog.objects.bulk_create([RemoteRealmAuditLog(**data) for data in data_list])
|
|
server_id = 1
|
|
|
|
# Used in billing code
|
|
remote_server_counts = get_remote_server_guest_and_non_guest_count(
|
|
server_id=server_id, event_time=timezone_now()
|
|
)
|
|
self.assertEqual(remote_server_counts.non_guest_user_count, 73)
|
|
self.assertEqual(remote_server_counts.guest_user_count, 16)
|
|
|
|
# Used in remote activity view code
|
|
server_logs = get_remote_server_audit_logs()
|
|
remote_activity_counts = get_remote_customer_user_count(server_logs[server_id])
|
|
self.assertEqual(remote_activity_counts.non_guest_user_count, 73)
|
|
self.assertEqual(remote_activity_counts.guest_user_count, 16)
|
|
|
|
def test_remote_activity_with_robust_data(self) -> None:
|
|
def add_plan(customer: Customer, tier: int, fixed_price: bool = False) -> None:
|
|
if fixed_price:
|
|
plan = CustomerPlan.objects.create(
|
|
customer=customer,
|
|
billing_cycle_anchor=timezone_now(),
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
|
tier=tier,
|
|
fixed_price=10000,
|
|
next_invoice_date=add_months(timezone_now(), 12),
|
|
)
|
|
else:
|
|
if tier in (
|
|
CustomerPlan.TIER_SELF_HOSTED_BASE,
|
|
CustomerPlan.TIER_SELF_HOSTED_LEGACY,
|
|
CustomerPlan.TIER_SELF_HOSTED_COMMUNITY,
|
|
):
|
|
price_per_license = 0
|
|
else:
|
|
price_per_license = 1000
|
|
plan = CustomerPlan.objects.create(
|
|
customer=customer,
|
|
billing_cycle_anchor=timezone_now(),
|
|
billing_schedule=CustomerPlan.BILLING_SCHEDULE_ANNUAL,
|
|
tier=tier,
|
|
price_per_license=price_per_license,
|
|
next_invoice_date=add_months(timezone_now(), 12),
|
|
)
|
|
LicenseLedger.objects.create(
|
|
licenses=10,
|
|
licenses_at_next_renewal=10,
|
|
event_time=timezone_now(),
|
|
is_renewal=True,
|
|
plan=plan,
|
|
)
|
|
|
|
def add_audit_log_data(
|
|
server: RemoteZulipServer, remote_realm: RemoteRealm | None, realm_id: int | None
|
|
) -> None:
|
|
extra_data = {
|
|
RemoteRealmAuditLog.ROLE_COUNT: {
|
|
RemoteRealmAuditLog.ROLE_COUNT_HUMANS: {
|
|
UserProfile.ROLE_REALM_ADMINISTRATOR: 1,
|
|
UserProfile.ROLE_REALM_OWNER: 1,
|
|
UserProfile.ROLE_MODERATOR: 0,
|
|
UserProfile.ROLE_MEMBER: 0,
|
|
UserProfile.ROLE_GUEST: 1,
|
|
}
|
|
}
|
|
}
|
|
if remote_realm is not None:
|
|
RemoteRealmAuditLog.objects.create(
|
|
server=server,
|
|
remote_realm=remote_realm,
|
|
event_type=AuditLogEventType.USER_CREATED,
|
|
event_time=timezone_now() - timedelta(days=1),
|
|
extra_data=extra_data,
|
|
)
|
|
else:
|
|
RemoteRealmAuditLog.objects.create(
|
|
server=server,
|
|
realm_id=realm_id,
|
|
event_type=AuditLogEventType.USER_CREATED,
|
|
event_time=timezone_now() - timedelta(days=1),
|
|
extra_data=extra_data,
|
|
)
|
|
|
|
for i in range(6):
|
|
hostname = f"zulip-{i}.example.com"
|
|
remote_server = RemoteZulipServer.objects.create(
|
|
hostname=hostname, contact_email=f"admin@{hostname}", uuid=uuid.uuid4()
|
|
)
|
|
RemoteZulipServerAuditLog.objects.create(
|
|
event_type=AuditLogEventType.REMOTE_SERVER_CREATED,
|
|
server=remote_server,
|
|
event_time=remote_server.last_updated,
|
|
)
|
|
# We want at least one RemoteZulipServer that has no RemoteRealm
|
|
# as an example of a pre-8.0 release registered remote server.
|
|
if i > 2:
|
|
realm_name = f"realm-name-{i}"
|
|
realm_host = f"realm-host-{i}"
|
|
realm_uuid = uuid.uuid4()
|
|
RemoteRealm.objects.create(
|
|
server=remote_server,
|
|
uuid=realm_uuid,
|
|
host=realm_host,
|
|
name=realm_name,
|
|
realm_date_created=datetime(2023, 12, 1, tzinfo=timezone.utc),
|
|
)
|
|
|
|
# Remote server on legacy plan
|
|
server = RemoteZulipServer.objects.get(hostname="zulip-1.example.com")
|
|
customer = Customer.objects.create(remote_server=server)
|
|
add_plan(customer, tier=CustomerPlan.TIER_SELF_HOSTED_LEGACY)
|
|
add_audit_log_data(server, remote_realm=None, realm_id=2)
|
|
|
|
# Remote server paid plan - multiple realms
|
|
server = RemoteZulipServer.objects.get(hostname="zulip-2.example.com")
|
|
customer = Customer.objects.create(remote_server=server)
|
|
add_plan(customer, tier=CustomerPlan.TIER_SELF_HOSTED_BASIC)
|
|
add_audit_log_data(server, remote_realm=None, realm_id=3)
|
|
add_audit_log_data(server, remote_realm=None, realm_id=4)
|
|
add_audit_log_data(server, remote_realm=None, realm_id=5)
|
|
|
|
# Single remote realm on remote server - community plan
|
|
realm = RemoteRealm.objects.get(name="realm-name-3")
|
|
customer = Customer.objects.create(remote_realm=realm)
|
|
add_plan(customer, tier=CustomerPlan.TIER_SELF_HOSTED_COMMUNITY)
|
|
add_audit_log_data(realm.server, remote_realm=realm, realm_id=None)
|
|
|
|
# Single remote realm on remote server - paid plan
|
|
realm = RemoteRealm.objects.get(name="realm-name-4")
|
|
customer = Customer.objects.create(remote_realm=realm)
|
|
add_plan(customer, tier=CustomerPlan.TIER_SELF_HOSTED_BUSINESS)
|
|
add_audit_log_data(realm.server, remote_realm=realm, realm_id=None)
|
|
|
|
# Multiple remote realms on remote server - on different paid plans
|
|
realm = RemoteRealm.objects.get(name="realm-name-5")
|
|
customer = Customer.objects.create(remote_realm=realm)
|
|
add_plan(customer, tier=CustomerPlan.TIER_SELF_HOSTED_BASIC)
|
|
add_audit_log_data(realm.server, remote_realm=realm, realm_id=None)
|
|
|
|
remote_server = realm.server
|
|
realm_name = "realm-name-6"
|
|
realm_host = "realm-host-6"
|
|
realm_uuid = uuid.uuid4()
|
|
RemoteRealm.objects.create(
|
|
server=remote_server,
|
|
uuid=realm_uuid,
|
|
host=realm_host,
|
|
name=realm_name,
|
|
realm_date_created=datetime(2023, 12, 1, tzinfo=timezone.utc),
|
|
)
|
|
|
|
realm = RemoteRealm.objects.get(name="realm-name-6")
|
|
customer = Customer.objects.create(remote_realm=realm)
|
|
add_plan(customer, tier=CustomerPlan.TIER_SELF_HOSTED_BASIC, fixed_price=True)
|
|
add_audit_log_data(realm.server, remote_realm=realm, realm_id=None)
|
|
|
|
self.login("iago")
|
|
with self.assert_database_query_count(11):
|
|
result = self.client_get("/activity/remote")
|
|
self.assertEqual(result.status_code, 200)
|
|
|
|
with self.assert_database_query_count(7):
|
|
result = self.client_get(f"/activity/remote/logs/server/{remote_server.uuid}/")
|
|
self.assertEqual(result.status_code, 200)
|