keycloak-admin 0.6.5 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
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