Skip to content

Commit

Permalink
feat(tempest): add audit log events for tempest client ids (#83091)
Browse files Browse the repository at this point in the history
Add audit log events TEMPEST_CLIENT_ID_ADD/TEMPEST_CLIENT_ID_REMOVE for
when tempes client ids are created/deleted.

Closes getsentry/team-gdx#56
  • Loading branch information
constantinius authored Jan 9, 2025
1 parent f7793dc commit c13aed7
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 6 deletions.
17 changes: 17 additions & 0 deletions src/sentry/audit_log/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,20 @@
api_name="data-secrecy.reinstated",
)
)

default_manager.add(
AuditLogEvent(
event_id=1152,
name="TEMPEST_CLIENT_ID_ADD",
api_name="playstation-client-id.create",
template="added playstation client id {client_id}",
)
)
default_manager.add(
AuditLogEvent(
event_id=1153,
name="TEMPEST_CLIENT_ID_REMOVE",
api_name="playstation-client-id.remove",
template="removed playstation client id {client_id}",
)
)
13 changes: 11 additions & 2 deletions src/sentry/tempest/endpoints/tempest_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from rest_framework.request import Request
from rest_framework.response import Response

from sentry import features
from sentry import audit_log, features
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import region_silo_endpoint
Expand Down Expand Up @@ -50,9 +50,18 @@ def post(self, request: Request, project: Project) -> Response:
serializer = DRFTempestCredentialsSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
serializer.save(created_by_id=request.user.id, project=project)
credentials = serializer.save(created_by_id=request.user.id, project=project)
except IntegrityError:
return Response(
{"detail": "A credential with this client ID already exists."}, status=400
)

self.create_audit_entry(
request,
organization=project.organization,
target_object=credentials.id,
event=audit_log.get_event_id("TEMPEST_CLIENT_ID_ADD"),
data=credentials.get_audit_log_data(),
)

return Response(serializer.data, status=201)
19 changes: 17 additions & 2 deletions src/sentry/tempest/endpoints/tempest_credentials_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from rest_framework.request import Request
from rest_framework.response import Response

from sentry import features
from sentry import audit_log, features
from sentry.api.api_owners import ApiOwner
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import region_silo_endpoint
Expand Down Expand Up @@ -30,5 +30,20 @@ def delete(self, request: Request, project: Project, tempest_credentials_id: int
if not self.has_feature(request, project):
raise NotFound

TempestCredentials.objects.filter(project=project, id=tempest_credentials_id).delete()
try:
credentials = TempestCredentials.objects.get(project=project, id=tempest_credentials_id)
except TempestCredentials.DoesNotExist:
raise NotFound

data = credentials.get_audit_log_data()
credentials.delete()

self.create_audit_entry(
request,
organization=project.organization,
target_object=credentials.id,
event=audit_log.get_event_id("TEMPEST_CLIENT_ID_REMOVE"),
data=data,
)

return Response(status=204)
6 changes: 6 additions & 0 deletions src/sentry/tempest/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,9 @@ class Meta:
name="sentry_tempestcredentials_client_project_uniq",
)
]

def get_audit_log_data(self) -> dict:
return {
"project_id": self.project.id,
"client_id": self.client_id,
}
9 changes: 8 additions & 1 deletion tests/sentry/tempest/endpoints/test_tempest_credentials.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import patch

from sentry.tempest.models import TempestCredentials
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.features import Feature
Expand Down Expand Up @@ -38,7 +40,10 @@ def test_client_secret_is_obfuscated(self):
def test_unauthenticated_user_cant_access_endpoint(self):
self.get_error_response(self.project.organization.slug, self.project.slug)

def test_create_tempest_credentials(self):
@patch(
"sentry.tempest.endpoints.tempest_credentials.TempestCredentialsEndpoint.create_audit_entry"
)
def test_create_tempest_credentials(self, create_audit_entry):
with Feature({"organizations:tempest-access": True}):
self.login_as(self.user)
response = self.get_success_response(
Expand All @@ -54,6 +59,8 @@ def test_create_tempest_credentials(self):
assert creds_obj.project == self.project
assert creds_obj.created_by_id == self.user.id

create_audit_entry.assert_called()

def test_create_tempest_credentials_without_feature_flag(self):
self.login_as(self.user)
response = self.get_error_response(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import patch

from sentry.tempest.models import TempestCredentials
from sentry.testutils.cases import APITestCase
from sentry.testutils.helpers.features import Feature
Expand Down Expand Up @@ -29,7 +31,10 @@ def test_cant_access_endpoint_if_user_is_not_authenticated(self):
)
assert response.status_code == 401

def test_delete_tempest_credentials_as_org_admin(self):
@patch(
"sentry.tempest.endpoints.tempest_credentials_details.TempestCredentialsDetailsEndpoint.create_audit_entry"
)
def test_delete_tempest_credentials_as_org_admin(self, create_audit_entry):
with Feature({"organizations:tempest-access": True}):
self.login_as(self.user)
response = self.get_response(
Expand All @@ -41,6 +46,7 @@ def test_delete_tempest_credentials_as_org_admin(self):

assert response.status_code == 204
assert not TempestCredentials.objects.filter(id=self.tempest_credentials.id).exists()
create_audit_entry.assert_called()

def test_non_admin_cant_delete_credentials(self):
non_admin_user = self.create_user()
Expand Down

0 comments on commit c13aed7

Please sign in to comment.