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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +48 -0
- data/Gemfile.lock +1 -1
- data/README.md +109 -2
- data/lib/keycloak-admin.rb +9 -0
- data/lib/keycloak-admin/client/client.rb +11 -3
- data/lib/keycloak-admin/client/client_client.rb +24 -0
- data/lib/keycloak-admin/client/client_role_mappings_client.rb +20 -0
- data/lib/keycloak-admin/client/group_client.rb +46 -0
- data/lib/keycloak-admin/client/realm_client.rb +50 -0
- data/lib/keycloak-admin/client/role_client.rb +32 -0
- data/lib/keycloak-admin/client/user_client.rb +9 -2
- 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 +4 -0
- 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/user_resource.rb +18 -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 +48 -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/user_client_spec.rb +105 -14
- data/spec/representation/user_representation_spec.rb +15 -0
- data/spec/resource/user_resource_spec.rb +14 -0
- data/spec/spec_helper.rb +7 -0
- 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.
|
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.
|
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
|
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
|
-
:
|
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
|
|
@@ -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
|
@@ -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
|