Skip to content

Commit 8649b22

Browse files
feat(front): Role mapping for SAML JIT provisioned users (#123)
## 📝 Description <!-- Describe your changes in detail --> ## ✅ Checklist - [ ] I have tested this change - [ ] This change requires documentation update
1 parent 47aa9e6 commit 8649b22

File tree

13 files changed

+722
-316
lines changed

13 files changed

+722
-316
lines changed

ee/rbac/lib/internal_api/okta.pb.ex

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,37 +52,59 @@ defmodule InternalApi.Okta.GenerateScimTokenResponse do
5252
field(:token, 1, type: :string)
5353
end
5454

55-
defmodule InternalApi.Okta.SetUpGroupMappingRequest do
55+
defmodule InternalApi.Okta.SetUpMappingRequest do
5656
@moduledoc false
5757

5858
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"
5959

6060
field(:org_id, 1, type: :string, json_name: "orgId")
6161
field(:default_role_id, 2, type: :string, json_name: "defaultRoleId")
62-
field(:mappings, 3, repeated: true, type: InternalApi.Okta.GroupMapping)
62+
63+
field(:group_mapping, 3,
64+
repeated: true,
65+
type: InternalApi.Okta.GroupMapping,
66+
json_name: "groupMapping"
67+
)
68+
69+
field(:role_mapping, 4,
70+
repeated: true,
71+
type: InternalApi.Okta.RoleMapping,
72+
json_name: "roleMapping"
73+
)
6374
end
6475

65-
defmodule InternalApi.Okta.SetUpGroupMappingResponse do
76+
defmodule InternalApi.Okta.SetUpMappingResponse do
6677
@moduledoc false
6778

6879
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"
6980
end
7081

71-
defmodule InternalApi.Okta.DescribeGroupMappingRequest do
82+
defmodule InternalApi.Okta.DescribeMappingRequest do
7283
@moduledoc false
7384

7485
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"
7586

7687
field(:org_id, 1, type: :string, json_name: "orgId")
7788
end
7889

79-
defmodule InternalApi.Okta.DescribeGroupMappingResponse do
90+
defmodule InternalApi.Okta.DescribeMappingResponse do
8091
@moduledoc false
8192

8293
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"
8394

8495
field(:default_role_id, 1, type: :string, json_name: "defaultRoleId")
85-
field(:mappings, 2, repeated: true, type: InternalApi.Okta.GroupMapping)
96+
97+
field(:group_mapping, 2,
98+
repeated: true,
99+
type: InternalApi.Okta.GroupMapping,
100+
json_name: "groupMapping"
101+
)
102+
103+
field(:role_mapping, 3,
104+
repeated: true,
105+
type: InternalApi.Okta.RoleMapping,
106+
json_name: "roleMapping"
107+
)
86108
end
87109

88110
defmodule InternalApi.Okta.GroupMapping do
@@ -94,6 +116,15 @@ defmodule InternalApi.Okta.GroupMapping do
94116
field(:okta_group_id, 2, type: :string, json_name: "oktaGroupId")
95117
end
96118

119+
defmodule InternalApi.Okta.RoleMapping do
120+
@moduledoc false
121+
122+
use Protobuf, syntax: :proto3, protoc_gen_elixir_version: "0.13.0"
123+
124+
field(:semaphore_role_id, 1, type: :string, json_name: "semaphoreRoleId")
125+
field(:okta_role_id, 2, type: :string, json_name: "oktaRoleId")
126+
end
127+
97128
defmodule InternalApi.Okta.ListRequest do
98129
@moduledoc false
99130

@@ -160,16 +191,12 @@ defmodule InternalApi.Okta.Okta.Service do
160191

161192
rpc(:Destroy, InternalApi.Okta.DestroyRequest, InternalApi.Okta.DestroyResponse)
162193

163-
rpc(
164-
:SetUpGroupMapping,
165-
InternalApi.Okta.SetUpGroupMappingRequest,
166-
InternalApi.Okta.SetUpGroupMappingResponse
167-
)
194+
rpc(:SetUpMapping, InternalApi.Okta.SetUpMappingRequest, InternalApi.Okta.SetUpMappingResponse)
168195

169196
rpc(
170-
:DescribeGroupMapping,
171-
InternalApi.Okta.DescribeGroupMappingRequest,
172-
InternalApi.Okta.DescribeGroupMappingResponse
197+
:DescribeMapping,
198+
InternalApi.Okta.DescribeMappingRequest,
199+
InternalApi.Okta.DescribeMappingResponse
173200
)
174201
end
175202

ee/rbac/lib/rbac/grpc_servers/okta_server.ex

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ defmodule Rbac.GrpcServers.OktaServer do
1010
GenerateScimTokenResponse,
1111
ListResponse,
1212
ListUsersResponse,
13-
SetUpGroupMappingResponse,
14-
DescribeGroupMappingResponse,
15-
GroupMapping
13+
SetUpMappingResponse,
14+
DescribeMappingResponse,
15+
GroupMapping,
16+
RoleMapping
1617
}
1718

1819
@manage_okta_permission "organization.okta.manage"
@@ -128,69 +129,89 @@ defmodule Rbac.GrpcServers.OktaServer do
128129
end)
129130
end
130131

131-
def set_up_group_mapping(req, _stream) do
132-
observe("set_up_group_mapping", fn ->
132+
def set_up_mapping(req, _stream) do
133+
observe("set_up_mapping", fn ->
133134
validate_uuid!(req.org_id)
134135

135-
mappings =
136-
Enum.map(req.mappings, fn mapping ->
136+
group_mappings =
137+
Enum.map(req.group_mapping, fn mapping ->
137138
%{
138139
idp_group_id: mapping.okta_group_id,
139140
semaphore_group_id: mapping.semaphore_group_id
140141
}
141142
end)
142143

143-
case Rbac.Okta.IdpGroupMapping.create_or_update(req.org_id, mappings, req.default_role_id) do
144+
role_mappings =
145+
Enum.map(req.role_mapping, fn mapping ->
146+
%{
147+
idp_role_id: mapping.okta_role_id,
148+
semaphore_role_id: mapping.semaphore_role_id
149+
}
150+
end)
151+
152+
case Rbac.Okta.IdpGroupMapping.create_or_update(
153+
req.org_id,
154+
group_mappings,
155+
role_mappings,
156+
req.default_role_id
157+
) do
144158
{:ok, _mapping} ->
145-
Logger.info("Group mappings created/updated for org #{req.org_id}")
146-
%SetUpGroupMappingResponse{}
159+
Logger.info("Group and role mappings created/updated for org #{req.org_id}")
160+
%SetUpMappingResponse{}
147161

148162
{:error, %Ecto.Changeset{} = changeset} ->
149163
Logger.error(
150-
"Error while setting up group mappings for org #{req.org_id}: #{inspect(changeset)}"
164+
"Error while setting up mappings for org #{req.org_id}: #{inspect(changeset)}"
151165
)
152166

153167
grpc_error!(
154168
:failed_precondition,
155-
"Failed to save group mappings: Invalid"
169+
"Failed to save mappings: Invalid"
156170
)
157171

158172
error ->
159-
Logger.error("Unknown error while setting up group mappings: #{inspect(error)}")
160-
grpc_error!(:unknown, "Unknown error while setting up group mappings")
173+
Logger.error("Unknown error while setting up mappings: #{inspect(error)}")
174+
grpc_error!(:unknown, "Unknown error while setting up mappings")
161175
end
162176
end)
163177
end
164178

165-
def describe_group_mapping(req, _stream) do
166-
observe("list_group_mappings", fn ->
179+
def describe_mapping(req, _stream) do
180+
observe("describe_mapping", fn ->
167181
validate_uuid!(req.org_id)
168182

169183
case Rbac.Okta.IdpGroupMapping.get_for_organization(req.org_id) do
170-
{:ok, idp_group_mapping} ->
184+
{:ok, idp_mapping} ->
171185
# Convert from our internal format to protobuf messages
172186
group_mapping =
173-
Enum.map(idp_group_mapping.group_mapping, fn mapping ->
187+
Enum.map(idp_mapping.group_mapping, fn mapping ->
174188
%GroupMapping{
175189
okta_group_id: mapping.idp_group_id,
176190
semaphore_group_id: mapping.semaphore_group_id
177191
}
178192
end)
179193

180-
%DescribeGroupMappingResponse{
181-
mappings: group_mapping,
182-
default_role_id: idp_group_mapping.default_role_id
194+
role_mapping =
195+
Enum.map(idp_mapping.role_mapping || [], fn mapping ->
196+
%RoleMapping{
197+
okta_role_id: mapping.idp_role_id,
198+
semaphore_role_id: mapping.semaphore_role_id
199+
}
200+
end)
201+
202+
%DescribeMappingResponse{
203+
group_mapping: group_mapping,
204+
role_mapping: role_mapping,
205+
default_role_id: idp_mapping.default_role_id
183206
}
184207

185208
{:error, :not_found} ->
186-
%DescribeGroupMappingResponse{mappings: []}
209+
%DescribeMappingResponse{group_mapping: [], role_mapping: []}
187210

188211
error ->
189-
Logger.error(
190-
"Error while listing group mappings for org #{req.org_id}: #{inspect(error)}"
191-
)
212+
Logger.error("Error while describing mappings for org #{req.org_id}: #{inspect(error)}")
192213

193-
grpc_error!(:unknown, "Unknown error while listing group mappings")
214+
grpc_error!(:unknown, "Unknown error while describing mappings")
194215
end
195216
end)
196217
end

ee/rbac/lib/rbac/okta/idp_group_mapping.ex

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,36 @@ defmodule Rbac.Okta.IdpGroupMapping do
77
alias Rbac.Repo.IdpGroupMapping
88

99
@doc """
10-
Creates or updates group mappings for an organization.
10+
Creates or updates mappings for an organization.
1111
1212
## Parameters
1313
* organization_id - The ID of the organization
1414
* group_mappings - A list of maps with idp_group_id and semaphore_group_id keys
15-
* default_role_id - The default role ID to use when no group mapping matches
15+
* role_mappings - A list of maps with idp_role_id and semaphore_role_id keys
16+
* default_role_id - The default role ID to use when no mappings match
1617
1718
## Returns
1819
* `{:ok, mapping}` - The created or updated mapping
1920
* `{:error, changeset}` - Error with changeset containing validation errors
2021
"""
21-
def create_or_update(organization_id, group_mapping, default_role_id)
22-
when is_list(group_mapping) do
22+
def create_or_update(organization_id, group_mapping, role_mapping \\ [], default_role_id)
23+
when is_list(group_mapping) and is_list(role_mapping) do
2324
require Logger
24-
Logger.info("req: group_mapping: #{inspect(group_mapping)}")
25+
26+
Logger.info(
27+
"req: group_mapping: #{inspect(group_mapping)}, role_mapping: #{inspect(role_mapping)}"
28+
)
2529

2630
IdpGroupMapping.insert_or_update(
2731
organization_id: organization_id,
2832
group_mapping: group_mapping,
33+
role_mapping: role_mapping,
2934
default_role_id: default_role_id
3035
)
3136
end
3237

3338
@doc """
34-
Retrieves group mappings for an organization.
39+
Retrieves mappings for an organization.
3540
3641
## Parameters
3742
* organization_id - The ID of the organization
@@ -61,7 +66,7 @@ defmodule Rbac.Okta.IdpGroupMapping do
6166
{:ok, mapping} ->
6267
# Create a lookup map for faster search
6368
lookup_map =
64-
Enum.reduce(mapping.group_mappings, %{}, fn m, acc ->
69+
Enum.reduce(mapping.group_mapping, %{}, fn m, acc ->
6570
Map.put(acc, m.idp_group_id, m.semaphore_group_id)
6671
end)
6772

@@ -82,4 +87,42 @@ defmodule Rbac.Okta.IdpGroupMapping do
8287
error
8388
end
8489
end
90+
91+
@doc """
92+
Maps IDP roles to Semaphore roles.
93+
94+
## Parameters
95+
* organization_id - The ID of the organization
96+
* idp_roles - List of IDP role identifiers
97+
98+
## Returns
99+
* `{:ok, semaphore_roles}` - List of mapped Semaphore role IDs
100+
* `{:error, :not_found}` - No mapping found for the organization
101+
"""
102+
def map_roles(organization_id, idp_roles) when is_list(idp_roles) do
103+
case get_for_organization(organization_id) do
104+
{:ok, mapping} ->
105+
# Create a lookup map for faster search
106+
lookup_map =
107+
Enum.reduce(mapping.role_mapping || [], %{}, fn m, acc ->
108+
Map.put(acc, m.idp_role_id, m.semaphore_role_id)
109+
end)
110+
111+
# Find matching semaphore roles
112+
mapped_roles =
113+
idp_roles
114+
|> Enum.reduce([], fn idp_role, acc ->
115+
case Map.get(lookup_map, idp_role) do
116+
nil -> acc
117+
semaphore_role -> [semaphore_role | acc]
118+
end
119+
end)
120+
|> Enum.uniq()
121+
122+
{:ok, mapped_roles}
123+
124+
error ->
125+
error
126+
end
127+
end
85128
end

0 commit comments

Comments
 (0)