-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathusers_scim.ex
More file actions
137 lines (117 loc) · 4.01 KB
/
users_scim.ex
File metadata and controls
137 lines (117 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
defmodule OkrApp.UsersScim do
alias OkrApp.Users.{User, UserStore}
# TODO: Find a way to replace this with a real module reference
def user_module(), do: User
def get_paged_list(filter: filter, per_page: per_page, start: start_index) do
users = UserStore.all()
start_index = max(start_index, 0)
users
|> apply_filter(String.split(filter, " "))
|> Enum.drop(start_index)
|> Enum.take(per_page)
end
def add_user(%User{} = user) do
Map.from_struct(user)
|> UserStore.add()
end
def update_user(%User{id: _} = user) do
Map.from_struct(user)
|> UserStore.add()
end
def get_user(id) do
UserStore.find(id)
end
def scim_resource_to_user(
resource = %{
"externalId" => id,
"userName" => user_name,
"name" => name_map,
"emails" => emails,
"active" => active
}
) do
enterprise_user = Map.get(resource, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", %{})
%User{
id: id |> clean_value(),
user_name: user_name |> clean_value(),
active: active |> clean_value(),
family_name: Map.get(name_map, "familyName") |> clean_value(),
given_name: Map.get(name_map, "givenName") |> clean_value(),
middle_name: Map.get(name_map, "middleName") |> clean_value(),
manager_id: enterprise_user |> Map.get("manager", %{}) |> Map.get("value", nil) |> clean_value(),
manager_display_name: enterprise_user |> Map.get("manager", %{}) |> Map.get("displayName", nil) |> clean_value(),
department: enterprise_user |> Map.get("department") |> clean_value(),
roles: Map.get(resource, "roles", []),
emails: emails
}
end
def user_to_scim_resource(%User{
id: id,
user_name: user_name,
family_name: family_name,
given_name: given_name,
middle_name: middle_name,
active: active,
emails: emails,
roles: roles,
manager_id: manager_id,
manager_display_name: manager_display_name,
department: department
}) do
base_user = %{
schemas: [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"
],
id: id,
userName: user_name,
name: %{
familyName: family_name,
givenName: given_name,
middleName: middle_name
},
emails: emails,
active: active,
meta: %{
resourceType: "User",
location: "/scim/v2/Users/#{id}"
},
roles: roles
}
enterprise_attributes = %{
department: department,
manager: nil
}
manager = manager_map(manager_id, manager_display_name)
enterprise_attributes = %{enterprise_attributes | manager: manager}
enterprise_attributes =
enterprise_attributes
|> Enum.reject(fn {_, v} -> is_nil(v) end)
|> Enum.into(%{})
Map.put(base_user, :"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", enterprise_attributes)
end
# Okta forces us to use the complex PATCH API to update `active` rather than PUT API
# However, it should only ever be a replace on the active field
def apply_scim_operations_to_user(user = %User{}, [%{"op" => "replace", "value" => %{"active" => active_value}}]) do
{:ok, %{user | active: active_value}}
end
# Okta will either send no filter or userName eq "userName"
defp apply_filter(users, [""]), do: users
defp apply_filter(users, ["userName", "eq", quoted_val]) do
value = String.trim(quoted_val, "\"")
Enum.filter(users, fn %User{user_name: user_name} -> user_name == value end)
end
# Empty strings are treated as non-values in Okta
defp clean_value(value) when value == "" or value == nil, do: nil
defp clean_value(value), do: value
# https://tools.ietf.org/html/rfc7643#section-4.3
defp manager_map(nil, nil), do: nil
defp manager_map(id, display_name) do
%{
value: id,
displayName: display_name
}
|> Enum.reject(fn {_, v} -> is_nil(v) end)
|> Enum.into(%{})
end
end