Skip to content

Commit

Permalink
Add CS views for creating and deleting service accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
Oksamies committed Aug 20, 2024
1 parent ed9f73e commit 1d5c762
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 0 deletions.
234 changes: 234 additions & 0 deletions django/thunderstore/api/cyberstorm/tests/test_team.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rest_framework.test import APIClient

from thunderstore.account.factories import ServiceAccountFactory
from thunderstore.account.models.service_account import ServiceAccount
from thunderstore.core.types import UserType
from thunderstore.repository.factories import TeamFactory, TeamMemberFactory
from thunderstore.repository.models.team import Team, TeamMember
Expand Down Expand Up @@ -533,6 +534,239 @@ def test_team_service_account_list_api_view__for_member__sorts_results(
assert result[2]["name"] == charlie.first_name


@pytest.mark.django_db
def test_team_service_account_create__when_creating_a_service_account__succeeds(
api_client: APIClient,
team_owner: TeamMember,
):
api_client.force_authenticate(team_owner.user)

response = api_client.post(
f"/api/cyberstorm/team/{team_owner.team.name}/service-account/create/",
json.dumps(
{
"nickname": "CoolestTeamServiceAccountName",
}
),
content_type="application/json",
)

assert response.status_code == 200
response_json = response.json()
assert response_json["nickname"] == "CoolestTeamServiceAccountName"
assert response_json["team_name"] == team_owner.team.name
assert response_json["api_token"][:4] == "tss_"
assert (
ServiceAccount.objects.filter(
owner__name=team_owner.team.name,
user__first_name="CoolestTeamServiceAccountName",
).count()
== 1
)


@pytest.mark.django_db
def test_team_service_account_create__when_creating_a_service_account__fails_because_nickname_too_long(
api_client: APIClient,
team_owner: TeamMember,
):
api_client.force_authenticate(team_owner.user)

response = api_client.post(
f"/api/cyberstorm/team/{team_owner.team.name}/service-account/create/",
json.dumps(
{
"nickname": "LongestCoolestTeamServiceAccountNameEver",
}
),
content_type="application/json",
)

assert response.status_code == 400
response_json = response.json()
assert response_json["nickname"] == [
"Ensure this value has at most 32 characters (it has 40)."
]
assert (
ServiceAccount.objects.filter(
owner__name=team_owner.team.name,
user__first_name="LongestCoolestTeamServiceAccountNameEver",
).count()
== 0
)


@pytest.mark.django_db
def test_team_service_account_create__when_creating_a_service_account__fails_because_user_is_not_authenticated(
api_client: APIClient,
team: Team,
):
response = api_client.post(
f"/api/cyberstorm/team/{team.name}/service-account/create/",
json.dumps(
{
"nickname": "CoolestTeamServiceAccountName",
}
),
content_type="application/json",
)

assert response.status_code == 401
response_json = response.json()
assert response_json["detail"] == "Authentication credentials were not provided."
assert (
ServiceAccount.objects.filter(
owner__name=team.name, user__first_name="CoolestTeamServiceAccountName"
).count()
== 0
)


@pytest.mark.django_db
def test_team_service_account_create__when_creating_a_service_account__fails_because_user_is_not_team_member(
api_client: APIClient,
team: Team,
):
non_team_user = User.objects.create()
api_client.force_authenticate(non_team_user)

response = api_client.post(
f"/api/cyberstorm/team/{team.name}/service-account/create/",
json.dumps(
{
"nickname": "CoolestTeamServiceAccountName",
}
),
content_type="application/json",
)

assert response.status_code == 400
response_json = response.json()
assert response_json["team"] == [
"Select a valid choice. That choice is not one of the available choices."
]
assert (
ServiceAccount.objects.filter(
owner__name=team.name, user__first_name="CoolestTeamServiceAccountName"
).count()
== 0
)


@pytest.mark.django_db
def test_team_service_account_create__when_creating_a_service_account__fails_because_user_is_not_team_owner(
api_client: APIClient,
team: Team,
team_member: TeamMember,
):

api_client.force_authenticate(team_member.user)

response = api_client.post(
f"/api/cyberstorm/team/{team.name}/service-account/create/",
json.dumps(
{
"nickname": "CoolestTeamServiceAccountName",
}
),
content_type="application/json",
)

assert response.status_code == 400
response_json = response.json()
assert response_json["team"] == ["Must be an owner to create a service account"]
assert (
ServiceAccount.objects.filter(
owner__name=team.name, user__first_name="CoolestTeamServiceAccountName"
).count()
== 0
)


@pytest.mark.django_db
def test_team_service_account_delete__when_deleting_a_service_account__succeeds(
api_client: APIClient,
team_owner: TeamMember,
service_account: ServiceAccount,
):
api_client.force_authenticate(team_owner.user)

response = api_client.post(
f"/api/cyberstorm/team/{service_account.owner.name}/service-account/delete/",
json.dumps({"service_account_uuid": str(service_account.uuid)}),
content_type="application/json",
)

assert response.status_code == 200
response_json = response.json()
assert response_json["detail"] == "Service account deleted"
assert ServiceAccount.objects.filter(uuid=service_account.uuid).count() == 0


@pytest.mark.django_db
def test_team_service_account_delete__when_deleting_a_service_account__fails_because_user_is_not_authenticated(
api_client: APIClient,
team: Team,
service_account: ServiceAccount,
):
response = api_client.post(
f"/api/cyberstorm/team/{team.name}/service-account/delete/",
json.dumps({"service_account_uuid": str(service_account.uuid)}),
content_type="application/json",
)

assert response.status_code == 401
response_json = response.json()
assert response_json["detail"] == "Authentication credentials were not provided."
assert ServiceAccount.objects.filter(uuid=service_account.uuid).count() == 1


@pytest.mark.django_db
def test_team_service_account_delete__when_deleting_a_service_account__fails_because_user_is_not_team_member(
api_client: APIClient,
team: Team,
service_account: ServiceAccount,
):
non_team_user = User.objects.create()
api_client.force_authenticate(non_team_user)

response = api_client.post(
f"/api/cyberstorm/team/{team.name}/service-account/delete/",
json.dumps({"service_account_uuid": str(service_account.uuid)}),
content_type="application/json",
)

assert response.status_code == 400
response_json = response.json()
assert response_json["service_account"] == [
"Select a valid choice. That choice is not one of the available choices."
]
assert ServiceAccount.objects.filter(uuid=service_account.uuid).count() == 1


@pytest.mark.django_db
def test_team_service_account_delete__when_deleting_a_service_account__fails_because_user_is_not_team_owner(
api_client: APIClient,
team_member: TeamMember,
team: Team,
service_account: ServiceAccount,
):
api_client.force_authenticate(team_member.user)

response = api_client.post(
f"/api/cyberstorm/team/{team.name}/service-account/delete/",
json.dumps({"service_account_uuid": str(service_account.uuid)}),
content_type="application/json",
)

assert response.status_code == 400
response_json = response.json()
assert response_json["service_account"] == [
"Must be an owner to delete a service account"
]
assert ServiceAccount.objects.filter(uuid=service_account.uuid).count() == 1


@pytest.mark.django_db
def test_team_member_add_api_view__when_adding_a_member__succeeds(
api_client: APIClient,
Expand Down
4 changes: 4 additions & 0 deletions django/thunderstore/api/cyberstorm/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
RemoveTeamMemberAPIView,
TeamAPIView,
TeamCreateAPIView,
TeamCreateServiceAccountAPIView,
TeamDeleteServiceAccountAPIView,
TeamMemberAddAPIView,
TeamMemberListAPIView,
TeamServiceAccountListAPIView,
Expand All @@ -38,6 +40,8 @@
"TeamMemberAddAPIView",
"TeamMemberListAPIView",
"TeamServiceAccountListAPIView",
"TeamCreateServiceAccountAPIView",
"TeamDeleteServiceAccountAPIView",
"PackageRatingRateAPIView",
"PackageDeprecateAPIView",
"PackageListingEditCategoriesAPIView",
Expand Down
83 changes: 83 additions & 0 deletions django/thunderstore/api/cyberstorm/views/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from thunderstore.account.forms import (
CreateServiceAccountForm,
DeleteServiceAccountForm,
)
from thunderstore.account.models.service_account import ServiceAccount
from thunderstore.api.cyberstorm.serializers import (
CyberstormServiceAccountSerializer,
Expand Down Expand Up @@ -318,3 +322,82 @@ def get_queryset(self) -> QuerySet[ServiceAccount]:
return ServiceAccount.objects.exclude(
~Q(owner__name__iexact=self.kwargs["team_id"]),
).select_related("user")


class TeamCreateServiceAccountRequestSerialiazer(serializers.Serializer):
nickname = serializers.CharField()


class TeamCreateServiceAccountResponseSerialiazer(serializers.Serializer):
nickname = serializers.CharField()
team_name = serializers.CharField()
api_token = serializers.CharField()


class TeamCreateServiceAccountAPIView(APIView):
@conditional_swagger_auto_schema(
request_body=TeamCreateServiceAccountRequestSerialiazer,
responses={200: TeamCreateServiceAccountResponseSerialiazer},
operation_id="cyberstorm.team.service-account.create",
tags=["cyberstorm"],
)
def post(self, request: HttpRequest, team_name: str):
serializer = TeamCreateServiceAccountRequestSerialiazer(data=request.data)
serializer.is_valid(raise_exception=True)
team = get_object_or_404(Team, name__iexact=team_name)
form = CreateServiceAccountForm(
user=request.user,
data={
**serializer.validated_data,
"team": team,
},
)

if form.is_valid():
service_account = form.save()
return Response(
TeamCreateServiceAccountResponseSerialiazer(
{
"nickname": service_account.nickname,
"team_name": service_account.owner.name,
"api_token": form.api_token,
}
).data
)
else:
raise ValidationError(form.errors)


class TeamDeleteServiceAccountRequestSerialiazer(serializers.Serializer):
service_account_uuid = serializers.CharField()


class TeamDeleteServiceAccountResponseSerialiazer(serializers.Serializer):
detail = serializers.CharField()


class TeamDeleteServiceAccountAPIView(APIView):
@conditional_swagger_auto_schema(
request_body=TeamDeleteServiceAccountRequestSerialiazer,
responses={200: TeamDeleteServiceAccountResponseSerialiazer},
operation_id="cyberstorm.team.service-account.delete",
tags=["cyberstorm"],
)
def post(self, request: HttpRequest, team_name: str):
serializer = TeamDeleteServiceAccountRequestSerialiazer(data=request.data)
serializer.is_valid(raise_exception=True)
service_account = get_object_or_404(
ServiceAccount,
owner__name__iexact=team_name,
uuid=serializer.validated_data["service_account_uuid"],
)
form = DeleteServiceAccountForm(
user=request.user,
data={"service_account": service_account},
)

if form.is_valid():
form.save()
return Response({"detail": "Service account deleted"})
else:
raise ValidationError(form.errors)
12 changes: 12 additions & 0 deletions django/thunderstore/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
RemoveTeamMemberAPIView,
TeamAPIView,
TeamCreateAPIView,
TeamCreateServiceAccountAPIView,
TeamDeleteServiceAccountAPIView,
TeamMemberAddAPIView,
TeamMemberListAPIView,
TeamServiceAccountListAPIView,
Expand Down Expand Up @@ -140,6 +142,16 @@
TeamServiceAccountListAPIView.as_view(),
name="cyberstorm.team.service-account",
),
path(
"team/<str:team_name>/service-account/create/",
TeamCreateServiceAccountAPIView.as_view(),
name="cyberstorm.team.service-account.create",
),
path(
"team/<str:team_name>/service-account/delete/",
TeamDeleteServiceAccountAPIView.as_view(),
name="cyberstorm.team.service-account.delete",
),
path(
"current-user/delete/",
UserDeleteAPIView.as_view(),
Expand Down

0 comments on commit 1d5c762

Please sign in to comment.