keycloak-admin 0.6.5 → 0.7.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 +5 -5
- data/.gitignore +2 -1
- data/CHANGELOG.md +65 -0
- data/Dockerfile +1 -1
- data/Gemfile.lock +13 -4
- data/README.md +160 -9
- data/keycloak-admin.gemspec +3 -0
- data/lib/keycloak-admin.rb +13 -0
- data/lib/keycloak-admin/client/client.rb +13 -3
- data/lib/keycloak-admin/client/client_client.rb +24 -0
- data/lib/keycloak-admin/client/client_role_mappings_client.rb +32 -0
- data/lib/keycloak-admin/client/group_client.rb +46 -0
- data/lib/keycloak-admin/client/realm_client.rb +54 -0
- data/lib/keycloak-admin/client/role_client.rb +32 -0
- data/lib/keycloak-admin/client/role_mapper_client.rb +20 -0
- data/lib/keycloak-admin/client/token_client.rb +6 -1
- data/lib/keycloak-admin/client/user_client.rb +23 -6
- data/lib/keycloak-admin/configuration.rb +1 -1
- data/lib/keycloak-admin/representation/camel_json.rb +1 -1
- data/lib/keycloak-admin/representation/client_representation.rb +16 -0
- data/lib/keycloak-admin/representation/group_representation.rb +15 -0
- data/lib/keycloak-admin/representation/realm_representation.rb +14 -0
- data/lib/keycloak-admin/representation/representation.rb +6 -1
- data/lib/keycloak-admin/representation/role_representation.rb +17 -0
- data/lib/keycloak-admin/representation/user_representation.rb +13 -13
- data/lib/keycloak-admin/resource/base_role_containing_resource.rb +26 -0
- data/lib/keycloak-admin/resource/group_resource.rb +7 -0
- data/lib/keycloak-admin/resource/user_resource.rb +7 -0
- data/lib/keycloak-admin/version.rb +1 -1
- data/spec/client/client_client_spec.rb +53 -0
- data/spec/client/client_role_mappings_client_spec.rb +82 -0
- data/spec/client/client_spec.rb +28 -0
- data/spec/client/group_client_spec.rb +125 -0
- data/spec/client/realm_client_spec.rb +108 -0
- data/spec/client/role_client_spec.rb +83 -0
- data/spec/client/role_mapper_client_spec.rb +47 -0
- data/spec/client/token_client_spec.rb +32 -1
- data/spec/client/user_client_spec.rb +147 -0
- data/spec/configuration_spec.rb +2 -0
- data/spec/representation/user_representation_spec.rb +15 -0
- data/spec/resource/group_resource_spec.rb +14 -0
- data/spec/resource/user_resource_spec.rb +14 -0
- data/spec/spec_helper.rb +7 -0
- metadata +40 -5
@@ -0,0 +1,24 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class ClientClient < 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 list
|
10
|
+
response = execute_http do
|
11
|
+
RestClient::Resource.new(clients_url, @configuration.rest_client_options).get(headers)
|
12
|
+
end
|
13
|
+
JSON.parse(response).map { |client_as_hash| ClientRepresentation.from_hash(client_as_hash) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def clients_url(id=nil)
|
17
|
+
if id
|
18
|
+
"#{@realm_client.realm_admin_url}/clients/#{id}"
|
19
|
+
else
|
20
|
+
"#{@realm_client.realm_admin_url}/clients"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class ClientRoleMappingsClient < Client
|
3
|
+
def initialize(configuration, user_resource, client_id)
|
4
|
+
super(configuration)
|
5
|
+
@user_resource = user_resource
|
6
|
+
@client_id = client_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def list_available
|
10
|
+
response = execute_http do
|
11
|
+
RestClient::Resource.new(list_available_url, @configuration.rest_client_options).get(headers)
|
12
|
+
end
|
13
|
+
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(role_representation_list)
|
17
|
+
execute_http do
|
18
|
+
RestClient::Resource.new(base_url, @configuration.rest_client_options).post(
|
19
|
+
role_representation_list.to_json, headers
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def list_available_url
|
25
|
+
"#{@user_resource.resource_url}/role-mappings/clients/#{@client_id}/available"
|
26
|
+
end
|
27
|
+
|
28
|
+
def base_url
|
29
|
+
"#{@user_resource.resource_url}/role-mappings/clients/#{@client_id}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class GroupClient < 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 list
|
10
|
+
response = execute_http do
|
11
|
+
RestClient::Resource.new(groups_url, @configuration.rest_client_options).get(headers)
|
12
|
+
end
|
13
|
+
JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def create!(name, path = nil)
|
17
|
+
response = save(build(name, path))
|
18
|
+
created_id(response)
|
19
|
+
end
|
20
|
+
|
21
|
+
def save(group_representation)
|
22
|
+
execute_http do
|
23
|
+
RestClient::Resource.new(groups_url, @configuration.rest_client_options).post(
|
24
|
+
group_representation.to_json, headers
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def groups_url(id=nil)
|
30
|
+
if id
|
31
|
+
"#{@realm_client.realm_admin_url}/groups/#{id}"
|
32
|
+
else
|
33
|
+
"#{@realm_client.realm_admin_url}/groups"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def build(name, path)
|
40
|
+
group = GroupRepresentation.new
|
41
|
+
group.name = name
|
42
|
+
group.path = path
|
43
|
+
group
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -5,6 +5,36 @@ module KeycloakAdmin
|
|
5
5
|
@realm_name = realm_name
|
6
6
|
end
|
7
7
|
|
8
|
+
def list
|
9
|
+
response = execute_http do
|
10
|
+
RestClient::Resource.new(realm_list_url, @configuration.rest_client_options).get(headers)
|
11
|
+
end
|
12
|
+
JSON.parse(response).map { |realm_as_hash| RealmRepresentation.from_hash(realm_as_hash) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete
|
16
|
+
execute_http do
|
17
|
+
RestClient::Resource.new(realm_admin_url, @configuration.rest_client_options).delete(headers)
|
18
|
+
end
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(realm_representation)
|
23
|
+
execute_http do
|
24
|
+
RestClient::Resource.new(realm_list_url, @configuration.rest_client_options).post(
|
25
|
+
realm_representation.to_json, headers
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def update(realm_representation_body)
|
31
|
+
execute_http do
|
32
|
+
RestClient::Resource.new(realm_admin_url, @configuration.rest_client_options).put(
|
33
|
+
realm_representation_body.to_json, headers
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
8
38
|
def realm_url
|
9
39
|
if @realm_name
|
10
40
|
"#{server_url}/realms/#{@realm_name}"
|
@@ -21,6 +51,10 @@ module KeycloakAdmin
|
|
21
51
|
end
|
22
52
|
end
|
23
53
|
|
54
|
+
def realm_list_url
|
55
|
+
"#{server_url}/admin/realms"
|
56
|
+
end
|
57
|
+
|
24
58
|
def token
|
25
59
|
TokenClient.new(@configuration, self)
|
26
60
|
end
|
@@ -29,10 +63,30 @@ module KeycloakAdmin
|
|
29
63
|
ConfigurableTokenClient.new(@configuration, self)
|
30
64
|
end
|
31
65
|
|
66
|
+
def clients
|
67
|
+
ClientClient.new(@configuration, self)
|
68
|
+
end
|
69
|
+
|
70
|
+
def groups
|
71
|
+
GroupClient.new(@configuration, self)
|
72
|
+
end
|
73
|
+
|
74
|
+
def group(group_id)
|
75
|
+
GroupResource.new(@configuration, self, group_id)
|
76
|
+
end
|
77
|
+
|
78
|
+
def roles
|
79
|
+
RoleClient.new(@configuration, self)
|
80
|
+
end
|
81
|
+
|
32
82
|
def users
|
33
83
|
UserClient.new(@configuration, self)
|
34
84
|
end
|
35
85
|
|
86
|
+
def user(user_id)
|
87
|
+
UserResource.new(@configuration, self, user_id)
|
88
|
+
end
|
89
|
+
|
36
90
|
def name_defined?
|
37
91
|
!@realm_name.nil?
|
38
92
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class RoleClient < 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 list
|
10
|
+
response = execute_http do
|
11
|
+
RestClient::Resource.new(roles_url, @configuration.rest_client_options).get(headers)
|
12
|
+
end
|
13
|
+
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def save(role_representation)
|
17
|
+
execute_http do
|
18
|
+
RestClient::Resource.new(roles_url, @configuration.rest_client_options).post(
|
19
|
+
role_representation.to_json, headers
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def roles_url(id=nil)
|
25
|
+
if id
|
26
|
+
"#{@realm_client.realm_admin_url}/roles/#{id}"
|
27
|
+
else
|
28
|
+
"#{@realm_client.realm_admin_url}/roles"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class RoleMapperClient < Client
|
3
|
+
def initialize(configuration, user_resource)
|
4
|
+
super(configuration)
|
5
|
+
@user_resource = user_resource
|
6
|
+
end
|
7
|
+
|
8
|
+
def save_realm_level(role_representation_list)
|
9
|
+
execute_http do
|
10
|
+
RestClient::Resource.new(realm_level_url, @configuration.rest_client_options).post(
|
11
|
+
role_representation_list.to_json, headers
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def realm_level_url
|
17
|
+
"#{@user_resource.resource_url}/role-mappings/realm"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "rest-client"
|
2
|
+
|
1
3
|
module KeycloakAdmin
|
2
4
|
class TokenClient < Client
|
3
5
|
def initialize(configuration, realm_client)
|
@@ -16,7 +18,10 @@ module KeycloakAdmin
|
|
16
18
|
|
17
19
|
def get
|
18
20
|
response = execute_http do
|
19
|
-
RestClient.
|
21
|
+
RestClient::Resource.new(token_url, @configuration.rest_client_options).post(
|
22
|
+
@configuration.body_for_token_retrieval,
|
23
|
+
@configuration.headers_for_token_retrieval
|
24
|
+
)
|
20
25
|
end
|
21
26
|
TokenRepresentation.from_json(response.body)
|
22
27
|
end
|
@@ -13,7 +13,9 @@ module KeycloakAdmin
|
|
13
13
|
|
14
14
|
def save(user_representation)
|
15
15
|
execute_http do
|
16
|
-
RestClient.
|
16
|
+
RestClient::Resource.new(users_url, @configuration.rest_client_options).post(
|
17
|
+
user_representation.to_json, headers
|
18
|
+
)
|
17
19
|
end
|
18
20
|
user_representation
|
19
21
|
end
|
@@ -24,27 +26,37 @@ module KeycloakAdmin
|
|
24
26
|
|
25
27
|
def get(user_id)
|
26
28
|
response = execute_http do
|
27
|
-
RestClient.
|
29
|
+
RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).get(headers)
|
28
30
|
end
|
29
31
|
UserRepresentation.from_hash(JSON.parse(response))
|
30
|
-
rescue
|
31
|
-
nil
|
32
32
|
end
|
33
33
|
|
34
34
|
def search(query)
|
35
|
+
derived_headers = query ? headers.merge({params: { search: query }}) : headers
|
35
36
|
response = execute_http do
|
36
|
-
RestClient.
|
37
|
+
RestClient::Resource.new(users_url, @configuration.rest_client_options).get(derived_headers)
|
37
38
|
end
|
38
39
|
JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
|
39
40
|
end
|
40
41
|
|
42
|
+
def list
|
43
|
+
search(nil)
|
44
|
+
end
|
45
|
+
|
41
46
|
def delete(user_id)
|
42
47
|
execute_http do
|
43
|
-
RestClient.
|
48
|
+
RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).delete(headers)
|
44
49
|
end
|
45
50
|
true
|
46
51
|
end
|
47
52
|
|
53
|
+
def groups(user_id)
|
54
|
+
response = execute_http do
|
55
|
+
RestClient::Resource.new(groups_url(user_id), @configuration.rest_client_options).get(headers)
|
56
|
+
end
|
57
|
+
JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
|
58
|
+
end
|
59
|
+
|
48
60
|
def update_password(user_id, new_password)
|
49
61
|
execute_http do
|
50
62
|
RestClient.put(reset_password_url(user_id), {
|
@@ -81,6 +93,11 @@ module KeycloakAdmin
|
|
81
93
|
"#{users_url(user_id)}/reset-password"
|
82
94
|
end
|
83
95
|
|
96
|
+
def groups_url(user_id)
|
97
|
+
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
98
|
+
"#{users_url(user_id)}/groups"
|
99
|
+
end
|
100
|
+
|
84
101
|
def impersonation_url(user_id)
|
85
102
|
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
86
103
|
"#{users_url(user_id)}/impersonation"
|
@@ -2,7 +2,7 @@ require "base64"
|
|
2
2
|
|
3
3
|
module KeycloakAdmin
|
4
4
|
class Configuration
|
5
|
-
attr_accessor :server_url, :server_domain, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger
|
5
|
+
attr_accessor :server_url, :server_domain, :client_id, :client_secret, :client_realm_name, :use_service_account, :username, :password, :logger, :rest_client_options
|
6
6
|
|
7
7
|
def body_for_token_retrieval
|
8
8
|
if use_service_account
|
@@ -5,7 +5,7 @@ module KeycloakAdmin
|
|
5
5
|
if first_letter_in_uppercase
|
6
6
|
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
7
7
|
else
|
8
|
-
lower_case_and_underscored_word
|
8
|
+
lower_case_and_underscored_word[0] + camelize(lower_case_and_underscored_word)[1..-1]
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class ClientRepresentation < Representation
|
3
|
+
attr_accessor :id,
|
4
|
+
:name,
|
5
|
+
:client_id
|
6
|
+
# TODO: Add more attributes
|
7
|
+
|
8
|
+
def self.from_hash(hash)
|
9
|
+
client = new
|
10
|
+
client.id = hash["id"]
|
11
|
+
client.name = hash["name"]
|
12
|
+
client.client_id = hash["clientId"]
|
13
|
+
client
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class GroupRepresentation < Representation
|
3
|
+
attr_accessor :id,
|
4
|
+
:name,
|
5
|
+
:path
|
6
|
+
|
7
|
+
def self.from_hash(hash)
|
8
|
+
group = new
|
9
|
+
group.id = hash["id"]
|
10
|
+
group.name = hash["name"]
|
11
|
+
group.path = hash["path"]
|
12
|
+
group
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class RealmRepresentation < Representation
|
3
|
+
attr_accessor :id,
|
4
|
+
:realm
|
5
|
+
# TODO: Add more attributes
|
6
|
+
|
7
|
+
def self.from_hash(hash)
|
8
|
+
realm = new
|
9
|
+
realm.id = hash["id"]
|
10
|
+
realm.realm = hash["realm"]
|
11
|
+
realm
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,10 +1,15 @@
|
|
1
|
+
require "json"
|
1
2
|
require_relative "camel_json"
|
2
3
|
|
3
4
|
class Representation
|
4
5
|
include ::KeycloakAdmin::CamelJson
|
5
6
|
|
7
|
+
def as_json(options=nil)
|
8
|
+
Hash[instance_variables.map { |ivar| [ivar.to_s[1..-1], instance_variable_get(ivar)] }]
|
9
|
+
end
|
10
|
+
|
6
11
|
def to_json(options=nil)
|
7
|
-
snaked_hash = as_json
|
12
|
+
snaked_hash = as_json(options)
|
8
13
|
snaked_hash.keys.reduce({}) do |camelized_hash, key|
|
9
14
|
camelized_hash[camelize(key, false)] = snaked_hash[key]
|
10
15
|
camelized_hash
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module KeycloakAdmin
|
2
|
+
class RoleRepresentation < Representation
|
3
|
+
attr_accessor :id,
|
4
|
+
:name,
|
5
|
+
:composite,
|
6
|
+
:client_role
|
7
|
+
|
8
|
+
def self.from_hash(hash)
|
9
|
+
role = new
|
10
|
+
role.id = hash["id"]
|
11
|
+
role.name = hash["name"]
|
12
|
+
role.composite = hash["composite"]
|
13
|
+
role.client_role = hash["clientRole"]
|
14
|
+
role
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module KeycloakAdmin
|
2
2
|
class UserRepresentation < Representation
|
3
3
|
attr_accessor :id,
|
4
|
-
:
|
4
|
+
:created_timestamp,
|
5
5
|
:attributes,
|
6
6
|
:origin,
|
7
7
|
:username,
|
@@ -13,18 +13,18 @@ module KeycloakAdmin
|
|
13
13
|
:credentials
|
14
14
|
|
15
15
|
def self.from_hash(hash)
|
16
|
-
user
|
17
|
-
user.id
|
18
|
-
user.
|
19
|
-
user.origin
|
20
|
-
user.username
|
21
|
-
user.email
|
22
|
-
user.enabled
|
23
|
-
user.email_verified
|
24
|
-
user.first_name
|
25
|
-
user.last_name
|
26
|
-
user.attributes
|
27
|
-
user.credentials
|
16
|
+
user = new
|
17
|
+
user.id = hash["id"]
|
18
|
+
user.created_timestamp = hash["createdTimestamp"]
|
19
|
+
user.origin = hash["origin"]
|
20
|
+
user.username = hash["username"]
|
21
|
+
user.email = hash["email"]
|
22
|
+
user.enabled = hash["enabled"]
|
23
|
+
user.email_verified = hash["emailVerified"]
|
24
|
+
user.first_name = hash["firstName"]
|
25
|
+
user.last_name = hash["lastName"]
|
26
|
+
user.attributes = hash["attributes"]
|
27
|
+
user.credentials = hash["credentials"]&.map{ |hash| CredentialRepresentation.from_hash(hash) } || []
|
28
28
|
user
|
29
29
|
end
|
30
30
|
|