Skip to content

Commit f86b084

Browse files
committed
feat(rbac_ce): add service account subject type
1 parent 362305e commit f86b084

File tree

3 files changed

+100
-2
lines changed

3 files changed

+100
-2
lines changed

rbac/ce/lib/rbac/grpc_servers/rbac_server.ex

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ defmodule Rbac.GrpcServers.RbacServer do
298298
end
299299

300300
defp build_search_params(request, page) do
301+
Logger.info("build_search_params: #{inspect(request)}")
302+
301303
params =
302304
request
303305
|> Map.from_struct()
@@ -342,6 +344,9 @@ defmodule Rbac.GrpcServers.RbacServer do
342344
:member_has_role ->
343345
if valid_uuid?(value), do: Keyword.put(acc, :role_id, value), else: acc
344346

347+
:member_type ->
348+
Keyword.put(acc, :subject_type, value |> Atom.to_string() |> String.downcase())
349+
345350
_ ->
346351
acc
347352
end
@@ -376,10 +381,14 @@ defmodule Rbac.GrpcServers.RbacServer do
376381

377382
defp build_members_response(role_assignments, display_names_by_id) do
378383
Enum.map(role_assignments, fn assignment ->
384+
# Determine subject type - for CE, we support USER and SERVICE_ACCOUNT
385+
# We need to determine if this user_id is a service account
386+
subject_type = assignment.subject_type |> String.upcase() |> String.to_existing_atom()
387+
379388
%RBAC.ListMembersResponse.Member{
380389
subject: %RBAC.Subject{
381390
subject_id: assignment.user_id,
382-
subject_type: :USER,
391+
subject_type: subject_type,
383392
display_name: display_names_by_id[assignment.user_id] || ""
384393
},
385394
subject_role_bindings: [build_subject_role_binding(assignment)]

rbac/ce/lib/rbac/models/role_assignment.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ defmodule Rbac.Models.RoleAssignment do
1212
field(:role_id, :binary_id)
1313
field(:org_id, :binary_id, primary_key: true)
1414
field(:user_id, :binary_id, primary_key: true)
15+
field(:subject_type, :string, default: "user")
1516

1617
timestamps(inserted_at: :created_at, updated_at: :updated_at)
1718
end
1819

1920
@doc false
2021
def changeset(role_assignment, attrs) do
2122
role_assignment
22-
|> cast(attrs, [:user_id, :org_id, :role_id])
23+
|> cast(attrs, [:user_id, :org_id, :role_id, :subject_type])
2324
|> validate_required([:user_id, :org_id, :role_id])
2425
end
2526

@@ -45,6 +46,7 @@ defmodule Rbac.Models.RoleAssignment do
4546
:user_id -> from(r in query, where: r.user_id == ^value)
4647
:org_id -> from(r in query, where: r.org_id == ^value)
4748
:role_id -> from(r in query, where: r.role_id == ^value)
49+
:subject_type -> from(r in query, where: r.subject_type == ^value)
4850
_ -> query
4951
end
5052
end)

rbac/ce/test/rbac/grpc_servers/rbac_server_test.exs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,93 @@ defmodule Rbac.GrpcServers.RbacServerTest do
879879

880880
assert Enum.empty?(response.members)
881881
end
882+
883+
test "Should return only service accounts when member_type is SERVICE_ACCOUNT", %{
884+
channel: channel,
885+
org_id: org_id
886+
} do
887+
# Create a service account role assignment
888+
service_account_id = Ecto.UUID.generate()
889+
890+
Rbac.Support.RoleAssignmentsFixtures.role_assignment_fixture(%{
891+
user_id: service_account_id,
892+
role_id: Rbac.Roles.Member.role().id,
893+
org_id: org_id,
894+
subject_type: "service_account"
895+
})
896+
897+
# Mock the User API to return service account information
898+
GrpcMock.stub(UserMock, :describe_many, fn request, _ ->
899+
%InternalApi.User.DescribeManyResponse{
900+
users:
901+
[
902+
%InternalApi.User.User{
903+
id: service_account_id,
904+
name: "Test Service Account",
905+
creation_source: :SERVICE_ACCOUNT
906+
}
907+
]
908+
|> Enum.filter(fn user -> user.id in request.user_ids end)
909+
}
910+
end)
911+
912+
request = %InternalApi.RBAC.ListMembersRequest{
913+
org_id: org_id,
914+
member_type: :SERVICE_ACCOUNT,
915+
page: %InternalApi.RBAC.ListMembersRequest.Page{
916+
page_no: 1,
917+
page_size: 10
918+
}
919+
}
920+
921+
{:ok, response} = Stub.list_members(channel, request)
922+
923+
assert length(response.members) == 1
924+
925+
[member] = response.members
926+
assert member.subject.subject_type == :SERVICE_ACCOUNT
927+
assert member.subject.subject_id == service_account_id
928+
end
929+
930+
test "Should exclude service accounts by default when no member_type is specified", %{
931+
channel: channel,
932+
org_id: org_id,
933+
valid_requester: owner_user,
934+
member_user: member_user
935+
} do
936+
# Create a service account role assignment to ensure it's filtered out by default
937+
service_account_id = Ecto.UUID.generate()
938+
939+
Rbac.Support.RoleAssignmentsFixtures.role_assignment_fixture(%{
940+
user_id: service_account_id,
941+
role_id: Rbac.Roles.Admin.role().id,
942+
org_id: org_id,
943+
subject_type: "service_account"
944+
})
945+
946+
request = %InternalApi.RBAC.ListMembersRequest{
947+
org_id: org_id,
948+
page: %InternalApi.RBAC.ListMembersRequest.Page{
949+
page_no: 1,
950+
page_size: 10
951+
}
952+
}
953+
954+
{:ok, response} = Stub.list_members(channel, request)
955+
956+
# Should only return the 2 regular users, not the service account
957+
assert length(response.members) == 2
958+
959+
assert Enum.all?(response.members, fn member ->
960+
member.subject.subject_type == :USER and
961+
member.subject.subject_id in [member_user.user_id, owner_user.user_id]
962+
end)
963+
964+
# Verify service account is not in the results
965+
refute Enum.any?(response.members, fn member ->
966+
member.subject.subject_id == service_account_id
967+
end)
968+
end
882969
end
883970

884971
describe "count_members/2" do

0 commit comments

Comments
 (0)