keycloak-admin 0.7.0 → 0.7.1

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/CHANGELOG.md +48 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +109 -2
  6. data/lib/keycloak-admin.rb +9 -0
  7. data/lib/keycloak-admin/client/client.rb +11 -3
  8. data/lib/keycloak-admin/client/client_client.rb +24 -0
  9. data/lib/keycloak-admin/client/client_role_mappings_client.rb +20 -0
  10. data/lib/keycloak-admin/client/group_client.rb +46 -0
  11. data/lib/keycloak-admin/client/realm_client.rb +50 -0
  12. data/lib/keycloak-admin/client/role_client.rb +32 -0
  13. data/lib/keycloak-admin/client/user_client.rb +9 -2
  14. data/lib/keycloak-admin/representation/camel_json.rb +1 -1
  15. data/lib/keycloak-admin/representation/client_representation.rb +16 -0
  16. data/lib/keycloak-admin/representation/group_representation.rb +15 -0
  17. data/lib/keycloak-admin/representation/realm_representation.rb +14 -0
  18. data/lib/keycloak-admin/representation/representation.rb +4 -0
  19. data/lib/keycloak-admin/representation/role_representation.rb +17 -0
  20. data/lib/keycloak-admin/representation/user_representation.rb +13 -13
  21. data/lib/keycloak-admin/resource/user_resource.rb +18 -0
  22. data/lib/keycloak-admin/version.rb +1 -1
  23. data/spec/client/client_client_spec.rb +53 -0
  24. data/spec/client/client_role_mappings_client_spec.rb +48 -0
  25. data/spec/client/group_client_spec.rb +125 -0
  26. data/spec/client/realm_client_spec.rb +108 -0
  27. data/spec/client/role_client_spec.rb +83 -0
  28. data/spec/client/user_client_spec.rb +105 -14
  29. data/spec/representation/user_representation_spec.rb +15 -0
  30. data/spec/resource/user_resource_spec.rb +14 -0
  31. data/spec/spec_helper.rb +7 -0
  32. metadata +18 -2
@@ -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
@@ -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
@@ -30,12 +32,17 @@ module KeycloakAdmin
30
32
  end
31
33
 
32
34
  def search(query)
35
+ derived_headers = query ? headers.merge({params: { search: query }}) : headers
33
36
  response = execute_http do
34
- RestClient.get(users_url, headers.merge({params: { search: query }}))
37
+ RestClient::Resource.new(users_url, @configuration.rest_client_options).get(derived_headers)
35
38
  end
36
39
  JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
37
40
  end
38
41
 
42
+ def list
43
+ search(nil)
44
+ end
45
+
39
46
  def delete(user_id)
40
47
  execute_http do
41
48
  RestClient::Resource.new(users_url(user_id), @configuration.rest_client_options).delete(headers)
@@ -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
@@ -4,6 +4,10 @@ require_relative "camel_json"
4
4
  class Representation
5
5
  include ::KeycloakAdmin::CamelJson
6
6
 
7
+ def as_json
8
+ Hash[instance_variables.map { |ivar| [ivar.to_s[1..-1], instance_variable_get(ivar)] }]
9
+ end
10
+
7
11
  def to_json(options=nil)
8
12
  snaked_hash = as_json
9
13
  snaked_hash.keys.reduce({}) do |camelized_hash, key|
@@ -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
 
@@ -0,0 +1,18 @@
1
+ module KeycloakAdmin
2
+ class UserResource
3
+ def initialize(configuration, realm_client, id)
4
+ @configuration = configuration
5
+ raise ArgumentError.new("realm must be defined") unless realm_client.name_defined?
6
+ @realm_client = realm_client
7
+ @id = id
8
+ end
9
+
10
+ def resource_url
11
+ "#{@realm_client.realm_admin_url}/users/#{@id}"
12
+ end
13
+
14
+ def client_role_mappings(client_id)
15
+ ClientRoleMappingsClient.new(@configuration, self, client_id)
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module KeycloakAdmin
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -0,0 +1,53 @@
1
+ RSpec.describe KeycloakAdmin::ClientClient do
2
+ describe "#clients_url" do
3
+ let(:realm_name) { "valid-realm" }
4
+ let(:client_id) { nil }
5
+
6
+ before(:each) do
7
+ @built_url = KeycloakAdmin.realm(realm_name).clients.clients_url(client_id)
8
+ end
9
+
10
+ context "when client_id is not defined" do
11
+ let(:client_id) { nil }
12
+ it "return a proper url without client id" do
13
+ expect(@built_url).to eq "http://auth.service.io/auth/admin/realms/valid-realm/clients"
14
+ end
15
+ end
16
+
17
+ context "when client_id is defined" do
18
+ let(:client_id) { "95985b21-d884-4bbd-b852-cb8cd365afc2" }
19
+ it "return a proper url with the client id" do
20
+ expect(@built_url).to eq "http://auth.service.io/auth/admin/realms/valid-realm/clients/95985b21-d884-4bbd-b852-cb8cd365afc2"
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#list" do
26
+ let(:realm_name) { "valid-realm" }
27
+
28
+ before(:each) do
29
+ @client_client = KeycloakAdmin.realm(realm_name).clients
30
+
31
+ stub_token_client
32
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '[{"id":"test_client_id","name":"test_client_name"}]'
33
+ end
34
+
35
+ it "lists clients" do
36
+ clients = @client_client.list
37
+ expect(clients.length).to eq 1
38
+ expect(clients[0].name).to eq "test_client_name"
39
+ end
40
+
41
+ it "passes rest client options" do
42
+ rest_client_options = {verify_ssl: OpenSSL::SSL::VERIFY_NONE}
43
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
44
+
45
+ expect(RestClient::Resource).to receive(:new).with(
46
+ "http://auth.service.io/auth/admin/realms/valid-realm/clients", rest_client_options).and_call_original
47
+
48
+ clients = @client_client.list
49
+ expect(clients.length).to eq 1
50
+ expect(clients[0].name).to eq "test_client_name"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,48 @@
1
+ RSpec.describe KeycloakAdmin::ClientRoleMappingsClient do
2
+ describe "#available_url" do
3
+ let(:realm_name) { "valid-realm" }
4
+ let(:user_id) { "test_user" }
5
+ let(:client_id) { "test_client" }
6
+
7
+ before(:each) do
8
+ @built_url = KeycloakAdmin.realm(realm_name).user(user_id).client_role_mappings(client_id).list_available_url
9
+ end
10
+
11
+ it "return a proper url" do
12
+ expect(@built_url).to eq "http://auth.service.io/auth/admin/realms/valid-realm/users/test_user/role-mappings/clients/test_client/available"
13
+ end
14
+ end
15
+
16
+ describe "#list_available" do
17
+ let(:realm_name) { "valid-realm" }
18
+ let(:user_id) { "test_user" }
19
+ let(:client_id) { "test_client" }
20
+
21
+ before(:each) do
22
+ @client_role_mappings_client = KeycloakAdmin.realm(realm_name).user(user_id).client_role_mappings(client_id)
23
+
24
+ stub_token_client
25
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '[{"id":"test_role_id","name":"test_role_name"}]'
26
+ end
27
+
28
+ it "lists roles" do
29
+ roles = @client_role_mappings_client.list_available
30
+ expect(roles.length).to eq 1
31
+ expect(roles[0].name).to eq "test_role_name"
32
+ end
33
+
34
+ it "passes rest client options" do
35
+ rest_client_options = {verify_ssl: OpenSSL::SSL::VERIFY_NONE}
36
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
37
+
38
+ expect(RestClient::Resource).to receive(:new).with(
39
+ "http://auth.service.io/auth/admin/realms/valid-realm/users/test_user/role-mappings/clients/test_client/available",
40
+ rest_client_options
41
+ ).and_call_original
42
+
43
+ roles = @client_role_mappings_client.list_available
44
+ expect(roles.length).to eq 1
45
+ expect(roles[0].name).to eq "test_role_name"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,125 @@
1
+ RSpec.describe KeycloakAdmin::GroupClient do
2
+ describe "#groups_url" do
3
+ let(:realm_name) { "valid-realm" }
4
+ let(:group_id) { nil }
5
+
6
+ before(:each) do
7
+ @built_url = KeycloakAdmin.realm(realm_name).groups.groups_url(group_id)
8
+ end
9
+
10
+ context "when group_id is not defined" do
11
+ let(:group_id) { nil }
12
+ it "return a proper url without group id" do
13
+ expect(@built_url).to eq "http://auth.service.io/auth/admin/realms/valid-realm/groups"
14
+ end
15
+ end
16
+
17
+ context "when group_id is defined" do
18
+ let(:group_id) { "95985b21-d884-4bbd-b852-cb8cd365afc2" }
19
+ it "return a proper url with the group id" do
20
+ expect(@built_url).to eq "http://auth.service.io/auth/admin/realms/valid-realm/groups/95985b21-d884-4bbd-b852-cb8cd365afc2"
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#list" do
26
+ let(:realm_name) { "valid-realm" }
27
+
28
+ before(:each) do
29
+ @group_client = KeycloakAdmin.realm(realm_name).groups
30
+
31
+ stub_token_client
32
+ allow_any_instance_of(RestClient::Resource).to receive(:get).and_return '[{"id":"test_group_id","name":"test_group_name"}]'
33
+ end
34
+
35
+ it "lists groups" do
36
+ groups = @group_client.list
37
+ expect(groups.length).to eq 1
38
+ expect(groups[0].name).to eq "test_group_name"
39
+ end
40
+
41
+ it "passes rest client options" do
42
+ rest_client_options = {verify_ssl: OpenSSL::SSL::VERIFY_NONE}
43
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
44
+
45
+ expect(RestClient::Resource).to receive(:new).with(
46
+ "http://auth.service.io/auth/admin/realms/valid-realm/groups", rest_client_options).and_call_original
47
+
48
+ groups = @group_client.list
49
+ expect(groups.length).to eq 1
50
+ expect(groups[0].name).to eq "test_group_name"
51
+ end
52
+ end
53
+
54
+ describe "#save" do
55
+ let(:realm_name) { "valid-realm" }
56
+ let(:group) { KeycloakAdmin::GroupRepresentation.from_hash(
57
+ "name" => "test_group_name"
58
+ )}
59
+
60
+ before(:each) do
61
+ @group_client = KeycloakAdmin.realm(realm_name).groups
62
+
63
+ stub_token_client
64
+ response = double
65
+ allow(response).to receive(:headers).and_return(
66
+ { location: 'http://auth.service.io/auth/admin/realms/valid-realm/groups/be061c48-6edd-4783-a726-1a57d4bfa22b' }
67
+ )
68
+ expect_any_instance_of(RestClient::Resource).to receive(:post).with(group.to_json, anything).and_return response
69
+ end
70
+
71
+ it "saves a group" do
72
+ @group_client.save(group)
73
+ end
74
+
75
+ it "passes rest client options" do
76
+ rest_client_options = {verify_ssl: OpenSSL::SSL::VERIFY_NONE}
77
+ allow_any_instance_of(KeycloakAdmin::Configuration).to receive(:rest_client_options).and_return rest_client_options
78
+
79
+ expect(RestClient::Resource).to receive(:new).with(
80
+ "http://auth.service.io/auth/admin/realms/valid-realm/groups", rest_client_options).and_call_original
81
+
82
+ @group_client.save(group)
83
+ end
84
+ end
85
+
86
+ describe "#create" do
87
+ let(:realm_name) { "valid-realm" }
88
+
89
+ before(:each) do
90
+ @group_client = KeycloakAdmin.realm(realm_name).groups
91
+
92
+ stub_token_client
93
+ @response = double
94
+ allow(@response).to receive(:headers).and_return(
95
+ { location: 'http://auth.service.io/auth/admin/realms/valid-realm/groups/be061c48-6edd-4783-a726-1a57d4bfa22b' }
96
+ )
97
+ allow_any_instance_of(RestClient::Resource).to receive(:post).and_return @response
98
+ end
99
+
100
+ it "creates a group" do
101
+ stub_net_http_res(Net::HTTPCreated, 201, 'Created')
102
+
103
+ group_id = @group_client.create!("test_group_name")
104
+ expect(group_id).to eq 'be061c48-6edd-4783-a726-1a57d4bfa22b'
105
+ end
106
+
107
+ it "detects unexpected response to create a group" do
108
+ stub_net_http_res(Net::HTTPOK, 200, 'OK')
109
+
110
+ expect{ @group_client.create!("test_group_name") }.to raise_error(
111
+ 'Create method returned status OK (Code: 200); expected status: Created (201)'
112
+ )
113
+ end
114
+
115
+ def stub_net_http_res(res_class, code, message)
116
+ net_http_res = double
117
+ allow(net_http_res).to receive(:message).and_return message
118
+ allow(net_http_res).to receive(:code).and_return code
119
+ allow(net_http_res).to receive(:is_a?) do |target_class|
120
+ target_class == res_class
121
+ end
122
+ allow(@response).to receive(:net_http_res).and_return net_http_res
123
+ end
124
+ end
125
+ end