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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -1
  3. data/CHANGELOG.md +65 -0
  4. data/Dockerfile +1 -1
  5. data/Gemfile.lock +13 -4
  6. data/README.md +160 -9
  7. data/keycloak-admin.gemspec +3 -0
  8. data/lib/keycloak-admin.rb +13 -0
  9. data/lib/keycloak-admin/client/client.rb +13 -3
  10. data/lib/keycloak-admin/client/client_client.rb +24 -0
  11. data/lib/keycloak-admin/client/client_role_mappings_client.rb +32 -0
  12. data/lib/keycloak-admin/client/group_client.rb +46 -0
  13. data/lib/keycloak-admin/client/realm_client.rb +54 -0
  14. data/lib/keycloak-admin/client/role_client.rb +32 -0
  15. data/lib/keycloak-admin/client/role_mapper_client.rb +20 -0
  16. data/lib/keycloak-admin/client/token_client.rb +6 -1
  17. data/lib/keycloak-admin/client/user_client.rb +23 -6
  18. data/lib/keycloak-admin/configuration.rb +1 -1
  19. data/lib/keycloak-admin/representation/camel_json.rb +1 -1
  20. data/lib/keycloak-admin/representation/client_representation.rb +16 -0
  21. data/lib/keycloak-admin/representation/group_representation.rb +15 -0
  22. data/lib/keycloak-admin/representation/realm_representation.rb +14 -0
  23. data/lib/keycloak-admin/representation/representation.rb +6 -1
  24. data/lib/keycloak-admin/representation/role_representation.rb +17 -0
  25. data/lib/keycloak-admin/representation/user_representation.rb +13 -13
  26. data/lib/keycloak-admin/resource/base_role_containing_resource.rb +26 -0
  27. data/lib/keycloak-admin/resource/group_resource.rb +7 -0
  28. data/lib/keycloak-admin/resource/user_resource.rb +7 -0
  29. data/lib/keycloak-admin/version.rb +1 -1
  30. data/spec/client/client_client_spec.rb +53 -0
  31. data/spec/client/client_role_mappings_client_spec.rb +82 -0
  32. data/spec/client/client_spec.rb +28 -0
  33. data/spec/client/group_client_spec.rb +125 -0
  34. data/spec/client/realm_client_spec.rb +108 -0
  35. data/spec/client/role_client_spec.rb +83 -0
  36. data/spec/client/role_mapper_client_spec.rb +47 -0
  37. data/spec/client/token_client_spec.rb +32 -1
  38. data/spec/client/user_client_spec.rb +147 -0
  39. data/spec/configuration_spec.rb +2 -0
  40. data/spec/representation/user_representation_spec.rb +15 -0
  41. data/spec/resource/group_resource_spec.rb +14 -0
  42. data/spec/resource/user_resource_spec.rb +14 -0
  43. data/spec/spec_helper.rb +7 -0
  44. 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.post(token_url, @configuration.body_for_token_retrieval, @configuration.headers_for_token_retrieval)
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.post(users_url, user_representation.to_json, headers)
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.get(users_url(user_id), headers)
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.get(users_url, headers.merge({params: { search: query }}))
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.delete(users_url(user_id), headers)
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.first + camelize(lower_case_and_underscored_word)[1..-1]
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
- :created_at,
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 = new
17
- user.id = hash["id"]
18
- user.created_at = Time.at(hash["createdTimestamp"] / 1000).to_datetime
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) } || []
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