Skip to content

Commit 23cc5d0

Browse files
authored
refactor(guard): user can be deleted only if he owns no projects (#237)
1 parent 26d85b5 commit 23cc5d0

File tree

3 files changed

+102
-16
lines changed

3 files changed

+102
-16
lines changed

guard/lib/guard/api/project.ex

+27-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ defmodule Guard.Api.Project do
4343
end
4444
end
4545

46+
def user_has_any_project?(user_id) do
47+
{:ok, channel} = GRPC.Stub.connect(Application.fetch_env!(:guard, :projecthub_grpc_endpoint))
48+
49+
req = build_list_by_owner_id_request(user_id, 1)
50+
51+
case InternalApi.Projecthub.ProjectService.Stub.list(channel, req, timeout: 30_000) do
52+
{:ok, response} when response.metadata.status.code == 0 ->
53+
response.projects |> length > 0
54+
55+
_ ->
56+
false
57+
end
58+
end
59+
4660
defp delete_project(channel, project_id, org_id, user_id) do
4761
req =
4862
InternalApi.Projecthub.DestroyRequest.new(
@@ -62,12 +76,22 @@ defmodule Guard.Api.Project do
6276
end
6377
end
6478

65-
defp build_list_by_org_id_request(org_id, page) do
79+
defp build_list_by_org_id_request(org_id, page),
80+
do: build_list_request(org_id: org_id, page: page)
81+
82+
defp build_list_by_owner_id_request(owner_id, page),
83+
do: build_list_request(owner_id: owner_id, page: page)
84+
85+
def build_list_request(opts) do
6686
InternalApi.Projecthub.ListRequest.new(
67-
metadata: InternalApi.Projecthub.RequestMeta.new(org_id: org_id),
87+
metadata:
88+
InternalApi.Projecthub.RequestMeta.new(
89+
user_id: opts[:owner_id] || "",
90+
org_id: opts[:org_id] || ""
91+
),
6892
pagination:
6993
InternalApi.Projecthub.PaginationRequest.new(
70-
page: page,
94+
page: opts[:page] || 1,
7195
page_size: @project_page_size
7296
)
7397
)

guard/lib/guard/grpc_servers/user_server.ex

+4
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ defmodule Guard.GrpcServers.UserServer do
306306
User.User.new(id: user_id)
307307

308308
{:ok, user} ->
309+
if Guard.Api.Project.user_has_any_project?(user_id) do
310+
grpc_error!(:invalid_argument, "User #{user_id} is owner of projects.")
311+
end
312+
309313
handle_delete_with_owned_orgs(user.id)
310314
end
311315
end)

guard/test/guard/grpc_servers/user_server_test.exs

+71-13
Original file line numberDiff line numberDiff line change
@@ -590,22 +590,80 @@ defmodule Guard.GrpcServers.UserServerTest do
590590

591591
request = User.DeleteWithOwnedOrgsRequest.new(user_id: user.id)
592592

593-
{:ok, response} = channel |> Stub.delete_with_owned_orgs(request)
593+
with_mock InternalApi.Projecthub.ProjectService.Stub,
594+
list: fn _channel, _req, _opts ->
595+
{:ok,
596+
InternalApi.Projecthub.ListResponse.new(
597+
metadata: InternalApi.Projecthub.ResponseMeta.new(status: %{code: 0}),
598+
projects: []
599+
)}
600+
end do
601+
{:ok, response} = channel |> Stub.delete_with_owned_orgs(request)
602+
603+
id = user.id
604+
assert %User.User{id: ^id} = response
605+
606+
# check if the user is deleted
607+
assert nil == FrontRepo.get(FrontRepo.User, id)
608+
assert nil == FrontRepo.get(FrontRepo.RepoHostAccount, repo_host_account.id)
609+
assert nil == FrontRepo.get(FrontRepo.Member, member.id)
610+
611+
receive do
612+
{:user_deleted_test, received_message} ->
613+
user_deleted = User.UserDeleted.decode(received_message)
614+
assert user_deleted.user_id == user.id
615+
after
616+
5000 -> flunk("Timeout: Message not received within 5 seconds")
617+
end
618+
end
619+
end
594620

595-
id = user.id
596-
assert %User.User{id: ^id} = response
621+
test "delete_with_owned_orgs should not delete the user if he has owned projects", %{
622+
grpc_channel: channel
623+
} do
624+
{:ok, user} = Support.Factories.RbacUser.insert()
625+
{:ok, _oidc_user} = Support.Factories.OIDCUser.insert(user.id)
597626

598-
# check if the user is deleted
599-
assert nil == FrontRepo.get(FrontRepo.User, id)
600-
assert nil == FrontRepo.get(FrontRepo.RepoHostAccount, repo_host_account.id)
601-
assert nil == FrontRepo.get(FrontRepo.Member, member.id)
627+
{:ok, _} =
628+
Support.Members.insert_user(
629+
id: user.id,
630+
email: user.email,
631+
name: user.name
632+
)
602633

603-
receive do
604-
{:user_deleted_test, received_message} ->
605-
user_deleted = User.UserDeleted.decode(received_message)
606-
assert user_deleted.user_id == user.id
607-
after
608-
5000 -> flunk("Timeout: Message not received within 5 seconds")
634+
{:ok, _repo_host_account} =
635+
Support.Members.insert_repo_host_account(
636+
login: "test",
637+
name: "test",
638+
github_uid: "123123",
639+
user_id: user.id,
640+
token: "token",
641+
revoked: false,
642+
permission_scope: "repo"
643+
)
644+
645+
{:ok, _member} = Support.Members.insert_member(github_uid: "123123")
646+
647+
request = User.DeleteWithOwnedOrgsRequest.new(user_id: user.id)
648+
649+
with_mock InternalApi.Projecthub.ProjectService.Stub,
650+
list: fn _channel, _req, _opts ->
651+
{:ok,
652+
InternalApi.Projecthub.ListResponse.new(
653+
metadata: InternalApi.Projecthub.ResponseMeta.new(status: %{code: 0}),
654+
projects: [
655+
%InternalApi.Projecthub.Project{
656+
metadata: InternalApi.Projecthub.RequestMeta.new(user_id: user.id)
657+
}
658+
]
659+
)}
660+
end do
661+
{:error, grpc_error} = channel |> Stub.delete_with_owned_orgs(request)
662+
663+
assert %GRPC.RPCError{
664+
status: GRPC.Status.invalid_argument(),
665+
message: "User #{user.id} is owner of projects."
666+
} == grpc_error
609667
end
610668
end
611669

0 commit comments

Comments
 (0)