keycloak-admin 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/Dockerfile +24 -24
- data/.github/workflows/ci.yml +80 -83
- data/.gitignore +9 -9
- data/.rspec +2 -2
- data/CHANGELOG.md +194 -188
- data/Dockerfile +12 -12
- data/Gemfile +3 -3
- data/Gemfile.lock +51 -49
- data/MIT-LICENSE +20 -20
- data/README.md +748 -748
- data/bin/console +9 -9
- data/keycloak-admin.gemspec +24 -24
- data/lib/keycloak-admin/client/attack_detection_client.rb +41 -41
- data/lib/keycloak-admin/client/client.rb +56 -56
- data/lib/keycloak-admin/client/client_authz_permission_client.rb +80 -80
- data/lib/keycloak-admin/client/client_authz_policy_client.rb +75 -75
- data/lib/keycloak-admin/client/client_authz_resource_client.rb +92 -92
- data/lib/keycloak-admin/client/client_authz_scope_client.rb +70 -70
- data/lib/keycloak-admin/client/client_client.rb +71 -71
- data/lib/keycloak-admin/client/client_role_client.rb +20 -20
- data/lib/keycloak-admin/client/client_role_mappings_client.rb +32 -32
- data/lib/keycloak-admin/client/configurable_token_client.rb +35 -35
- data/lib/keycloak-admin/client/group_client.rb +148 -131
- data/lib/keycloak-admin/client/identity_provider_client.rb +51 -51
- data/lib/keycloak-admin/client/realm_client.rb +122 -122
- data/lib/keycloak-admin/client/role_client.rb +59 -59
- data/lib/keycloak-admin/client/role_mapper_client.rb +47 -45
- data/lib/keycloak-admin/client/token_client.rb +29 -29
- data/lib/keycloak-admin/client/user_client.rb +266 -266
- data/lib/keycloak-admin/configuration.rb +52 -52
- data/lib/keycloak-admin/representation/attack_detection_representation.rb +17 -17
- data/lib/keycloak-admin/representation/camel_json.rb +12 -12
- data/lib/keycloak-admin/representation/client_authz_permission_representation.rb +33 -33
- data/lib/keycloak-admin/representation/client_authz_policy_config_representation.rb +14 -14
- data/lib/keycloak-admin/representation/client_authz_policy_representation.rb +26 -26
- data/lib/keycloak-admin/representation/client_authz_resource_representation.rb +25 -25
- data/lib/keycloak-admin/representation/client_authz_scope_representation.rb +16 -16
- data/lib/keycloak-admin/representation/client_representation.rb +71 -71
- data/lib/keycloak-admin/representation/credential_representation.rb +38 -38
- data/lib/keycloak-admin/representation/federated_identity_representation.rb +15 -15
- data/lib/keycloak-admin/representation/group_representation.rb +21 -21
- data/lib/keycloak-admin/representation/identity_provider_mapper_representation.rb +19 -19
- data/lib/keycloak-admin/representation/identity_provider_representation.rb +67 -67
- data/lib/keycloak-admin/representation/impersonation_redirection_representation.rb +16 -16
- data/lib/keycloak-admin/representation/impersonation_representation.rb +43 -43
- data/lib/keycloak-admin/representation/protocol_mapper_representation.rb +19 -19
- data/lib/keycloak-admin/representation/realm_representation.rb +14 -14
- data/lib/keycloak-admin/representation/representation.rb +23 -23
- data/lib/keycloak-admin/representation/role_representation.rb +19 -19
- data/lib/keycloak-admin/representation/session_representation.rb +22 -22
- data/lib/keycloak-admin/representation/token_representation.rb +39 -39
- data/lib/keycloak-admin/representation/user_representation.rb +47 -47
- data/lib/keycloak-admin/resource/base_role_containing_resource.rb +28 -28
- data/lib/keycloak-admin/resource/group_resource.rb +11 -11
- data/lib/keycloak-admin/resource/user_resource.rb +7 -7
- data/lib/keycloak-admin/version.rb +3 -3
- data/lib/keycloak-admin.rb +84 -84
- data/spec/client/attack_detection_client_spec.rb +102 -102
- data/spec/client/client_authz_permission_client_spec.rb +170 -170
- data/spec/client/client_authz_policy_client_spec.rb +169 -169
- data/spec/client/client_authz_resource_client_spec.rb +150 -150
- data/spec/client/client_authz_scope_client_spec.rb +133 -133
- data/spec/client/client_client_spec.rb +133 -133
- data/spec/client/client_role_mappings_client_spec.rb +82 -82
- data/spec/client/client_spec.rb +28 -28
- data/spec/client/configurable_token_client_spec.rb +34 -34
- data/spec/client/group_client_spec.rb +328 -258
- data/spec/client/identity_provider_client_spec.rb +92 -92
- data/spec/client/realm_client_spec.rb +155 -155
- data/spec/client/role_client_spec.rb +79 -79
- data/spec/client/role_mapper_client_spec.rb +113 -68
- data/spec/client/token_client_spec.rb +68 -68
- data/spec/client/user_client_spec.rb +373 -373
- data/spec/configuration_spec.rb +113 -113
- data/spec/integration/client_authorization_spec.rb +93 -95
- data/spec/representation/attack_detection_representation_spec.rb +15 -15
- data/spec/representation/client_authz_permission_representation_spec.rb +52 -52
- data/spec/representation/client_authz_policy_representation_spec.rb +46 -46
- data/spec/representation/client_authz_resource_representation_spec.rb +33 -33
- data/spec/representation/client_authz_scope_representation_spec.rb +18 -18
- data/spec/representation/client_representation_spec.rb +119 -119
- data/spec/representation/group_representation_spec.rb +22 -22
- data/spec/representation/identity_provider_mapper_representation_spec.rb +24 -24
- data/spec/representation/identity_provider_representation_spec.rb +113 -113
- data/spec/representation/impersonation_representation_spec.rb +163 -163
- data/spec/representation/protocol_mapper_representation_spec.rb +57 -57
- data/spec/representation/role_representation_spec.rb +37 -37
- data/spec/representation/session_representation_spec.rb +15 -15
- data/spec/representation/user_representation_spec.rb +15 -15
- data/spec/resource/group_resource_spec.rb +14 -14
- data/spec/resource/user_resource_spec.rb +14 -14
- data/spec/spec_helper.rb +37 -37
- metadata +10 -10
|
@@ -1,266 +1,266 @@
|
|
|
1
|
-
module KeycloakAdmin
|
|
2
|
-
class UserClient < Client
|
|
3
|
-
def initialize(configuration, realm_client)
|
|
4
|
-
super(configuration)
|
|
5
|
-
raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
|
|
6
|
-
@realm_client = realm_client
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def create!(username, email, password, email_verified, locale, attributes={})
|
|
10
|
-
user = save(build(username, email, password, email_verified, locale, attributes))
|
|
11
|
-
search(user.email)&.first
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def save(user_representation)
|
|
15
|
-
execute_http do
|
|
16
|
-
RestClient::Resource.new(users_url, @configuration.rest_client_options).post(
|
|
17
|
-
create_payload(user_representation), headers
|
|
18
|
-
)
|
|
19
|
-
end
|
|
20
|
-
user_representation
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# pay attention that, since Keycloak 24.0.4, partial updates of attributes are not authorized anymore
|
|
24
|
-
def update(user_id, user_representation_body)
|
|
25
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
26
|
-
RestClient::Request.execute(
|
|
27
|
-
@configuration.rest_client_options.merge(
|
|
28
|
-
method: :put,
|
|
29
|
-
url: users_url(user_id),
|
|
30
|
-
payload: create_payload(user_representation_body),
|
|
31
|
-
headers: headers
|
|
32
|
-
)
|
|
33
|
-
)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def add_group(user_id, group_id)
|
|
37
|
-
RestClient::Request.execute(
|
|
38
|
-
@configuration.rest_client_options.merge(
|
|
39
|
-
method: :put,
|
|
40
|
-
url: "#{users_url(user_id)}/groups/#{group_id}",
|
|
41
|
-
payload: create_payload({}),
|
|
42
|
-
headers: headers
|
|
43
|
-
)
|
|
44
|
-
)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def remove_group(user_id, group_id)
|
|
48
|
-
RestClient::Request.execute(
|
|
49
|
-
@configuration.rest_client_options.merge(
|
|
50
|
-
method: :delete,
|
|
51
|
-
url: "#{users_url(user_id)}/groups/#{group_id}",
|
|
52
|
-
headers: headers
|
|
53
|
-
)
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def add_client_roles_on_user(user_id, client_id, role_representations)
|
|
58
|
-
execute_http do
|
|
59
|
-
RestClient::Resource.new(user_client_role_mappings_url(user_id, client_id), @configuration.rest_client_options).post(
|
|
60
|
-
create_payload(role_representations), headers
|
|
61
|
-
)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def get(user_id)
|
|
66
|
-
response = execute_http do
|
|
67
|
-
RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).get(headers)
|
|
68
|
-
end
|
|
69
|
-
UserRepresentation.from_hash(JSON.parse(response))
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
##
|
|
73
|
-
# Query can be a string or a hash.
|
|
74
|
-
# * String: It's used as search query
|
|
75
|
-
# * Hash: Used for complex search queries.
|
|
76
|
-
# For its documentation see: https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_users_resource
|
|
77
|
-
##
|
|
78
|
-
def search(query)
|
|
79
|
-
derived_headers = case query
|
|
80
|
-
when String
|
|
81
|
-
headers.merge({params: { search: query }})
|
|
82
|
-
when Hash
|
|
83
|
-
headers.merge({params: query })
|
|
84
|
-
else
|
|
85
|
-
headers
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
response = execute_http do
|
|
89
|
-
RestClient::Resource.new(users_url, @configuration.rest_client_options).get(derived_headers)
|
|
90
|
-
end
|
|
91
|
-
JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def list
|
|
95
|
-
search(nil)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def delete(user_id)
|
|
99
|
-
execute_http do
|
|
100
|
-
RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).delete(headers)
|
|
101
|
-
end
|
|
102
|
-
true
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def groups(user_id)
|
|
106
|
-
response = execute_http do
|
|
107
|
-
RestClient::Resource.new(groups_url(user_id), @configuration.rest_client_options).get(headers)
|
|
108
|
-
end
|
|
109
|
-
JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def update_password(user_id, new_password)
|
|
113
|
-
execute_http do
|
|
114
|
-
RestClient::Request.execute(
|
|
115
|
-
@configuration.rest_client_options.merge(
|
|
116
|
-
method: :put,
|
|
117
|
-
url: reset_password_url(user_id),
|
|
118
|
-
payload: { type: "password", value: new_password, temporary: false }.to_json,
|
|
119
|
-
headers: headers
|
|
120
|
-
)
|
|
121
|
-
)
|
|
122
|
-
end
|
|
123
|
-
user_id
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def forgot_password(user_id, lifespan=nil)
|
|
127
|
-
execute_actions_email(user_id, ["UPDATE_PASSWORD"], lifespan)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def execute_actions_email(user_id, actions=[], lifespan=nil, redirect_uri=nil, client_id=nil)
|
|
131
|
-
raise ArgumentError.new("client_id must be defined") if client_id.nil? && !redirect_uri.nil?
|
|
132
|
-
execute_http do
|
|
133
|
-
lifespan_param = lifespan.nil? ? "" : "&lifespan=#{lifespan.seconds}"
|
|
134
|
-
redirect_uri_param = redirect_uri.nil? ? "" : "&redirect_uri=#{redirect_uri}"
|
|
135
|
-
client_id_param = client_id.nil? ? "" : "client_id=#{client_id}"
|
|
136
|
-
RestClient.put("#{execute_actions_email_url(user_id)}?#{client_id_param}#{redirect_uri_param}#{lifespan_param}", create_payload(actions), headers)
|
|
137
|
-
end
|
|
138
|
-
user_id
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def impersonate(user_id)
|
|
142
|
-
impersonation = get_redirect_impersonation(user_id)
|
|
143
|
-
response = execute_http do
|
|
144
|
-
RestClient::Request.execute(
|
|
145
|
-
@configuration.rest_client_options.merge(
|
|
146
|
-
method: :post,
|
|
147
|
-
url: impersonation.impersonation_url,
|
|
148
|
-
payload: impersonation.body.to_json,
|
|
149
|
-
headers: impersonation.headers
|
|
150
|
-
)
|
|
151
|
-
)
|
|
152
|
-
end
|
|
153
|
-
ImpersonationRepresentation.from_response(response, @configuration.server_domain)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def sessions(user_id)
|
|
157
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
158
|
-
|
|
159
|
-
response = execute_http do
|
|
160
|
-
RestClient::Resource.new("#{users_url(user_id)}/sessions", @configuration.rest_client_options).get(headers)
|
|
161
|
-
end
|
|
162
|
-
JSON.parse(response).map { |session_as_hash| SessionRepresentation.from_hash(session_as_hash) }
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def logout(user_id)
|
|
166
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
167
|
-
|
|
168
|
-
execute_http do
|
|
169
|
-
RestClient::Request.execute(
|
|
170
|
-
@configuration.rest_client_options.merge(
|
|
171
|
-
method: :post,
|
|
172
|
-
url: logout_url(user_id),
|
|
173
|
-
headers: headers
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
end
|
|
177
|
-
true
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def get_redirect_impersonation(user_id)
|
|
181
|
-
ImpersonationRedirectionRepresentation.from_url(impersonation_url(user_id), headers)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
def link_idp(user_id, idp_id, idp_user_id, idp_username)
|
|
185
|
-
fed_id_rep = FederatedIdentityRepresentation.new
|
|
186
|
-
fed_id_rep.user_id = idp_user_id
|
|
187
|
-
fed_id_rep.user_name = idp_username
|
|
188
|
-
fed_id_rep.identity_provider = idp_id
|
|
189
|
-
|
|
190
|
-
execute_http do
|
|
191
|
-
RestClient::Request.execute(
|
|
192
|
-
@configuration.rest_client_options.merge(
|
|
193
|
-
method: :post,
|
|
194
|
-
url: federated_identity_url(user_id, idp_id),
|
|
195
|
-
payload: fed_id_rep.to_json,
|
|
196
|
-
headers: headers
|
|
197
|
-
)
|
|
198
|
-
)
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def unlink_idp(user_id, idp_id)
|
|
203
|
-
execute_http do
|
|
204
|
-
RestClient::Resource.new(federated_identity_url(user_id, idp_id), @configuration.rest_client_options).delete(headers)
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def users_url(id=nil)
|
|
209
|
-
if id
|
|
210
|
-
"#{@realm_client.realm_admin_url}/users/#{id}"
|
|
211
|
-
else
|
|
212
|
-
"#{@realm_client.realm_admin_url}/users"
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def user_client_role_mappings_url(user_id, client_id)
|
|
217
|
-
"#{users_url(user_id)}/role-mappings/clients/#{client_id}"
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def reset_password_url(user_id)
|
|
221
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
222
|
-
"#{users_url(user_id)}/reset-password"
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def execute_actions_email_url(user_id)
|
|
226
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
227
|
-
"#{users_url(user_id)}/execute-actions-email"
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def groups_url(user_id)
|
|
231
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
232
|
-
"#{users_url(user_id)}/groups"
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def impersonation_url(user_id)
|
|
236
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
237
|
-
"#{users_url(user_id)}/impersonation"
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def federated_identity_url(user_id, identity_provider)
|
|
241
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
242
|
-
raise ArgumentError.new("identity_provider must be defined") if identity_provider.nil?
|
|
243
|
-
"#{users_url(user_id)}/federated-identity/#{identity_provider}"
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def logout_url(user_id)
|
|
247
|
-
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
248
|
-
|
|
249
|
-
"#{users_url(user_id)}/logout"
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
private
|
|
253
|
-
|
|
254
|
-
def build(username, email, password, email_verified, locale, attributes={})
|
|
255
|
-
user = UserRepresentation.new
|
|
256
|
-
user.email = email
|
|
257
|
-
user.username = username
|
|
258
|
-
user.email_verified = email_verified
|
|
259
|
-
user.enabled = true
|
|
260
|
-
user.attributes = attributes || {}
|
|
261
|
-
user.attributes[:locale] = locale if locale
|
|
262
|
-
user.add_credential(CredentialRepresentation.from_password(password)) if !password.nil?
|
|
263
|
-
user
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
end
|
|
1
|
+
module KeycloakAdmin
|
|
2
|
+
class UserClient < Client
|
|
3
|
+
def initialize(configuration, realm_client)
|
|
4
|
+
super(configuration)
|
|
5
|
+
raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
|
|
6
|
+
@realm_client = realm_client
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def create!(username, email, password, email_verified, locale, attributes={})
|
|
10
|
+
user = save(build(username, email, password, email_verified, locale, attributes))
|
|
11
|
+
search(user.email)&.first
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def save(user_representation)
|
|
15
|
+
execute_http do
|
|
16
|
+
RestClient::Resource.new(users_url, @configuration.rest_client_options).post(
|
|
17
|
+
create_payload(user_representation), headers
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
user_representation
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# pay attention that, since Keycloak 24.0.4, partial updates of attributes are not authorized anymore
|
|
24
|
+
def update(user_id, user_representation_body)
|
|
25
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
26
|
+
RestClient::Request.execute(
|
|
27
|
+
@configuration.rest_client_options.merge(
|
|
28
|
+
method: :put,
|
|
29
|
+
url: users_url(user_id),
|
|
30
|
+
payload: create_payload(user_representation_body),
|
|
31
|
+
headers: headers
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def add_group(user_id, group_id)
|
|
37
|
+
RestClient::Request.execute(
|
|
38
|
+
@configuration.rest_client_options.merge(
|
|
39
|
+
method: :put,
|
|
40
|
+
url: "#{users_url(user_id)}/groups/#{group_id}",
|
|
41
|
+
payload: create_payload({}),
|
|
42
|
+
headers: headers
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def remove_group(user_id, group_id)
|
|
48
|
+
RestClient::Request.execute(
|
|
49
|
+
@configuration.rest_client_options.merge(
|
|
50
|
+
method: :delete,
|
|
51
|
+
url: "#{users_url(user_id)}/groups/#{group_id}",
|
|
52
|
+
headers: headers
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def add_client_roles_on_user(user_id, client_id, role_representations)
|
|
58
|
+
execute_http do
|
|
59
|
+
RestClient::Resource.new(user_client_role_mappings_url(user_id, client_id), @configuration.rest_client_options).post(
|
|
60
|
+
create_payload(role_representations), headers
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def get(user_id)
|
|
66
|
+
response = execute_http do
|
|
67
|
+
RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).get(headers)
|
|
68
|
+
end
|
|
69
|
+
UserRepresentation.from_hash(JSON.parse(response))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Query can be a string or a hash.
|
|
74
|
+
# * String: It's used as search query
|
|
75
|
+
# * Hash: Used for complex search queries.
|
|
76
|
+
# For its documentation see: https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_users_resource
|
|
77
|
+
##
|
|
78
|
+
def search(query)
|
|
79
|
+
derived_headers = case query
|
|
80
|
+
when String
|
|
81
|
+
headers.merge({params: { search: query }})
|
|
82
|
+
when Hash
|
|
83
|
+
headers.merge({params: query })
|
|
84
|
+
else
|
|
85
|
+
headers
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
response = execute_http do
|
|
89
|
+
RestClient::Resource.new(users_url, @configuration.rest_client_options).get(derived_headers)
|
|
90
|
+
end
|
|
91
|
+
JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def list
|
|
95
|
+
search(nil)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def delete(user_id)
|
|
99
|
+
execute_http do
|
|
100
|
+
RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).delete(headers)
|
|
101
|
+
end
|
|
102
|
+
true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def groups(user_id)
|
|
106
|
+
response = execute_http do
|
|
107
|
+
RestClient::Resource.new(groups_url(user_id), @configuration.rest_client_options).get(headers)
|
|
108
|
+
end
|
|
109
|
+
JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def update_password(user_id, new_password)
|
|
113
|
+
execute_http do
|
|
114
|
+
RestClient::Request.execute(
|
|
115
|
+
@configuration.rest_client_options.merge(
|
|
116
|
+
method: :put,
|
|
117
|
+
url: reset_password_url(user_id),
|
|
118
|
+
payload: { type: "password", value: new_password, temporary: false }.to_json,
|
|
119
|
+
headers: headers
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
user_id
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def forgot_password(user_id, lifespan=nil)
|
|
127
|
+
execute_actions_email(user_id, ["UPDATE_PASSWORD"], lifespan)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def execute_actions_email(user_id, actions=[], lifespan=nil, redirect_uri=nil, client_id=nil)
|
|
131
|
+
raise ArgumentError.new("client_id must be defined") if client_id.nil? && !redirect_uri.nil?
|
|
132
|
+
execute_http do
|
|
133
|
+
lifespan_param = lifespan.nil? ? "" : "&lifespan=#{lifespan.seconds}"
|
|
134
|
+
redirect_uri_param = redirect_uri.nil? ? "" : "&redirect_uri=#{redirect_uri}"
|
|
135
|
+
client_id_param = client_id.nil? ? "" : "client_id=#{client_id}"
|
|
136
|
+
RestClient.put("#{execute_actions_email_url(user_id)}?#{client_id_param}#{redirect_uri_param}#{lifespan_param}", create_payload(actions), headers)
|
|
137
|
+
end
|
|
138
|
+
user_id
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def impersonate(user_id)
|
|
142
|
+
impersonation = get_redirect_impersonation(user_id)
|
|
143
|
+
response = execute_http do
|
|
144
|
+
RestClient::Request.execute(
|
|
145
|
+
@configuration.rest_client_options.merge(
|
|
146
|
+
method: :post,
|
|
147
|
+
url: impersonation.impersonation_url,
|
|
148
|
+
payload: impersonation.body.to_json,
|
|
149
|
+
headers: impersonation.headers
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
ImpersonationRepresentation.from_response(response, @configuration.server_domain)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def sessions(user_id)
|
|
157
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
158
|
+
|
|
159
|
+
response = execute_http do
|
|
160
|
+
RestClient::Resource.new("#{users_url(user_id)}/sessions", @configuration.rest_client_options).get(headers)
|
|
161
|
+
end
|
|
162
|
+
JSON.parse(response).map { |session_as_hash| SessionRepresentation.from_hash(session_as_hash) }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def logout(user_id)
|
|
166
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
167
|
+
|
|
168
|
+
execute_http do
|
|
169
|
+
RestClient::Request.execute(
|
|
170
|
+
@configuration.rest_client_options.merge(
|
|
171
|
+
method: :post,
|
|
172
|
+
url: logout_url(user_id),
|
|
173
|
+
headers: headers
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
end
|
|
177
|
+
true
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def get_redirect_impersonation(user_id)
|
|
181
|
+
ImpersonationRedirectionRepresentation.from_url(impersonation_url(user_id), headers)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def link_idp(user_id, idp_id, idp_user_id, idp_username)
|
|
185
|
+
fed_id_rep = FederatedIdentityRepresentation.new
|
|
186
|
+
fed_id_rep.user_id = idp_user_id
|
|
187
|
+
fed_id_rep.user_name = idp_username
|
|
188
|
+
fed_id_rep.identity_provider = idp_id
|
|
189
|
+
|
|
190
|
+
execute_http do
|
|
191
|
+
RestClient::Request.execute(
|
|
192
|
+
@configuration.rest_client_options.merge(
|
|
193
|
+
method: :post,
|
|
194
|
+
url: federated_identity_url(user_id, idp_id),
|
|
195
|
+
payload: fed_id_rep.to_json,
|
|
196
|
+
headers: headers
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def unlink_idp(user_id, idp_id)
|
|
203
|
+
execute_http do
|
|
204
|
+
RestClient::Resource.new(federated_identity_url(user_id, idp_id), @configuration.rest_client_options).delete(headers)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def users_url(id=nil)
|
|
209
|
+
if id
|
|
210
|
+
"#{@realm_client.realm_admin_url}/users/#{id}"
|
|
211
|
+
else
|
|
212
|
+
"#{@realm_client.realm_admin_url}/users"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def user_client_role_mappings_url(user_id, client_id)
|
|
217
|
+
"#{users_url(user_id)}/role-mappings/clients/#{client_id}"
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def reset_password_url(user_id)
|
|
221
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
222
|
+
"#{users_url(user_id)}/reset-password"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def execute_actions_email_url(user_id)
|
|
226
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
227
|
+
"#{users_url(user_id)}/execute-actions-email"
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def groups_url(user_id)
|
|
231
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
232
|
+
"#{users_url(user_id)}/groups"
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def impersonation_url(user_id)
|
|
236
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
237
|
+
"#{users_url(user_id)}/impersonation"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def federated_identity_url(user_id, identity_provider)
|
|
241
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
242
|
+
raise ArgumentError.new("identity_provider must be defined") if identity_provider.nil?
|
|
243
|
+
"#{users_url(user_id)}/federated-identity/#{identity_provider}"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def logout_url(user_id)
|
|
247
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
248
|
+
|
|
249
|
+
"#{users_url(user_id)}/logout"
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
private
|
|
253
|
+
|
|
254
|
+
def build(username, email, password, email_verified, locale, attributes={})
|
|
255
|
+
user = UserRepresentation.new
|
|
256
|
+
user.email = email
|
|
257
|
+
user.username = username
|
|
258
|
+
user.email_verified = email_verified
|
|
259
|
+
user.enabled = true
|
|
260
|
+
user.attributes = attributes || {}
|
|
261
|
+
user.attributes[:locale] = locale if locale
|
|
262
|
+
user.add_credential(CredentialRepresentation.from_password(password)) if !password.nil?
|
|
263
|
+
user
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
require "base64"
|
|
2
|
-
|
|
3
|
-
module KeycloakAdmin
|
|
4
|
-
class Configuration
|
|
5
|
-
attr_accessor :server_url, :server_domain, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger, :rest_client_options
|
|
6
|
-
|
|
7
|
-
def body_for_token_retrieval
|
|
8
|
-
if use_service_account
|
|
9
|
-
body_for_service_account
|
|
10
|
-
else
|
|
11
|
-
body_for_username_and_password
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def headers_for_token_retrieval
|
|
16
|
-
if use_service_account
|
|
17
|
-
headers_for_service_account
|
|
18
|
-
else
|
|
19
|
-
headers_for_username_and_password
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def body_for_service_account
|
|
26
|
-
{
|
|
27
|
-
grant_type: "client_credentials"
|
|
28
|
-
}
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def body_for_username_and_password
|
|
32
|
-
{
|
|
33
|
-
username: username,
|
|
34
|
-
password: password,
|
|
35
|
-
grant_type: "password",
|
|
36
|
-
client_id: client_id,
|
|
37
|
-
client_secret: client_secret
|
|
38
|
-
}
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def headers_for_service_account
|
|
42
|
-
id_and_secret = Base64::strict_encode64("#{client_id}:#{client_secret}")
|
|
43
|
-
{
|
|
44
|
-
Authorization: "Basic #{id_and_secret}"
|
|
45
|
-
}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def headers_for_username_and_password
|
|
49
|
-
{}
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
1
|
+
require "base64"
|
|
2
|
+
|
|
3
|
+
module KeycloakAdmin
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :server_url, :server_domain, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger, :rest_client_options
|
|
6
|
+
|
|
7
|
+
def body_for_token_retrieval
|
|
8
|
+
if use_service_account
|
|
9
|
+
body_for_service_account
|
|
10
|
+
else
|
|
11
|
+
body_for_username_and_password
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def headers_for_token_retrieval
|
|
16
|
+
if use_service_account
|
|
17
|
+
headers_for_service_account
|
|
18
|
+
else
|
|
19
|
+
headers_for_username_and_password
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def body_for_service_account
|
|
26
|
+
{
|
|
27
|
+
grant_type: "client_credentials"
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def body_for_username_and_password
|
|
32
|
+
{
|
|
33
|
+
username: username,
|
|
34
|
+
password: password,
|
|
35
|
+
grant_type: "password",
|
|
36
|
+
client_id: client_id,
|
|
37
|
+
client_secret: client_secret
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def headers_for_service_account
|
|
42
|
+
id_and_secret = Base64::strict_encode64("#{client_id}:#{client_secret}")
|
|
43
|
+
{
|
|
44
|
+
Authorization: "Basic #{id_and_secret}"
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def headers_for_username_and_password
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
module KeycloakAdmin
|
|
2
|
-
class AttackDetectionRepresentation < Representation
|
|
3
|
-
attr_accessor :num_failures,
|
|
4
|
-
:disabled,
|
|
5
|
-
:last_ip_failure,
|
|
6
|
-
:last_failure
|
|
7
|
-
|
|
8
|
-
def self.from_hash(hash)
|
|
9
|
-
rep = new
|
|
10
|
-
rep.num_failures = hash["numFailures"]
|
|
11
|
-
rep.disabled = hash["disabled"]
|
|
12
|
-
rep.last_ip_failure = hash["lastIPFailure"]
|
|
13
|
-
rep.last_failure = hash["lastFailure"]
|
|
14
|
-
rep
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
1
|
+
module KeycloakAdmin
|
|
2
|
+
class AttackDetectionRepresentation < Representation
|
|
3
|
+
attr_accessor :num_failures,
|
|
4
|
+
:disabled,
|
|
5
|
+
:last_ip_failure,
|
|
6
|
+
:last_failure
|
|
7
|
+
|
|
8
|
+
def self.from_hash(hash)
|
|
9
|
+
rep = new
|
|
10
|
+
rep.num_failures = hash["numFailures"]
|
|
11
|
+
rep.disabled = hash["disabled"]
|
|
12
|
+
rep.last_ip_failure = hash["lastIPFailure"]
|
|
13
|
+
rep.last_failure = hash["lastFailure"]
|
|
14
|
+
rep
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|