keycloak-ruby-client 0.0.15 → 0.0.20
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/README.md +27 -0
- data/keycloak-ruby-client.gemspec +3 -0
- data/lib/keycloak/api.rb +1 -0
- data/lib/keycloak/api/client_resources.rb +20 -3
- data/lib/keycloak/api/keycloak_api_extension_resources.rb +28 -0
- data/lib/keycloak/api/realm_resources.rb +3 -1
- data/lib/keycloak/api/user_resources.rb +26 -0
- data/lib/keycloak/client.rb +3 -1
- data/lib/keycloak/graphql.rb +3 -0
- data/lib/keycloak/graphql/keycloak_representation_iterator_connection.rb +101 -0
- data/lib/keycloak/realm.rb +3 -0
- data/lib/keycloak/user_entity.rb +5 -1
- data/lib/keycloak/version.rb +1 -1
- metadata +48 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '086dfbaf42ef3339dd5860e717be6490f625baa3'
|
4
|
+
data.tar.gz: c32725910ba174a2722839bf913b4c2d69f9785d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff0f354f26cbfce2d3ef617abe6d56e0a62c5acc99d5a254a52a95cd70ad72e64de0c34fd82fc236863bb1c0ea3732ed979d9cd8b4a2ab7e690cc6c12c5cfe06
|
7
|
+
data.tar.gz: 373f5165819f01ae672d30e87e378f3dacdc98bdfe2db63c814885b3f6ede60d6778ecfd3dad75405fe648de1dbde94e360912780914b4da2b39a0cf92c43e4d
|
data/README.md
CHANGED
@@ -109,6 +109,8 @@ client.find_users.each { |user| puts user.to_json } # no risk of out of memory
|
|
109
109
|
|
110
110
|
```
|
111
111
|
|
112
|
+
For more examples, please take a look at [spec/api/](./spec/api/)
|
113
|
+
|
112
114
|
### Connect your rails user model with keycloak user entity:
|
113
115
|
|
114
116
|
Firstly, we create a user model with `bundle exec rails g model user`
|
@@ -150,6 +152,31 @@ puts user.user_info
|
|
150
152
|
puts user.realm_roles
|
151
153
|
```
|
152
154
|
|
155
|
+
### GraphQL (optional)
|
156
|
+
|
157
|
+
Integrate `RepresentationIterator` into `graphql` to support GraphQL Connections
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
require 'keycloak/graphql'
|
161
|
+
```
|
162
|
+
|
163
|
+
Now you can implement a GraphQL API to find users as the following:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
field :users, Types::KeycloakUserRepresentationType.connection_type, null: true, max_page_size: 30 do
|
167
|
+
argument :briefRepresentation, Boolean, required: false
|
168
|
+
argument :email, String, required: false
|
169
|
+
argument :firstName, String, required: false
|
170
|
+
argument :lastName, String, required: false
|
171
|
+
argument :search, String, required: false
|
172
|
+
argument :username, String, required: false
|
173
|
+
end
|
174
|
+
|
175
|
+
def users(**kwargs)
|
176
|
+
Keycloak::Realm.your_realm.client.find_users(**kwargs)
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
153
180
|
## Development
|
154
181
|
|
155
182
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -39,9 +39,12 @@ Gem::Specification.new do |spec|
|
|
39
39
|
spec.add_dependency "rails", ">= 4.0"
|
40
40
|
spec.add_dependency "rest-client", "~> 2.0"
|
41
41
|
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
42
|
+
spec.add_dependency "jwt"
|
43
|
+
spec.add_dependency "json-jwt"
|
42
44
|
|
43
45
|
spec.add_development_dependency "bundler", "~> 2.0"
|
44
46
|
spec.add_development_dependency "rake", "~> 10.0"
|
45
47
|
spec.add_development_dependency "rspec"
|
46
48
|
spec.add_development_dependency "faker"
|
49
|
+
spec.add_development_dependency "factory_bot"
|
47
50
|
end
|
data/lib/keycloak/api.rb
CHANGED
@@ -4,11 +4,14 @@ module Keycloak
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
include Concerns::APIUtil
|
6
6
|
|
7
|
+
def client_resources_url
|
8
|
+
"#{admin_realm_url}/clients"
|
9
|
+
end
|
10
|
+
|
7
11
|
# @param client_id [String] client-id (not id of client)
|
8
12
|
# @return [Keycloak::Model::ClientRepresentation] client representation
|
9
13
|
def find_client_by_client_id(client_id)
|
10
|
-
|
11
|
-
data = JSON.parse(get(url, params: { clientId: client_id }).body)
|
14
|
+
data = JSON.parse(get(client_resources_url, params: { clientId: client_id }).body)
|
12
15
|
data[0] && Model::ClientRepresentation.new(data[0])
|
13
16
|
rescue RestClient::NotFound
|
14
17
|
nil
|
@@ -17,11 +20,25 @@ module Keycloak
|
|
17
20
|
# @param id [String] id of client (not client-id)
|
18
21
|
# @return [Keycloak::Model::ClientRepresentation] client representation
|
19
22
|
def find_client_by_id(id)
|
20
|
-
url =
|
23
|
+
url = client_resources_url + "/#{id}"
|
21
24
|
Model::ClientRepresentation.new JSON.parse(get(url).body)
|
22
25
|
rescue RestClient::NotFound
|
23
26
|
nil
|
24
27
|
end
|
28
|
+
|
29
|
+
# @param client_rep [Keycloak::Model::UserRepresentation] client representation
|
30
|
+
# @return [String] id of client
|
31
|
+
def create_client(client_rep)
|
32
|
+
res = post(client_resources_url, client_rep.to_json, headers: {content_type: :json})
|
33
|
+
res.headers[:location].split("/")[-1]
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param id [String] id of client (not client-id)
|
37
|
+
# @param client_rep [Keycloak::Model::UserRepresentation] client representation
|
38
|
+
def update_client(id, client_rep)
|
39
|
+
url = client_resources_url + "/#{id}"
|
40
|
+
put(url, client_rep.to_json, headers: {content_type: :json})
|
41
|
+
end
|
25
42
|
end
|
26
43
|
end
|
27
44
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Keycloak API extension resource from https://github.com/FX-HAO/keycloak-api-extension
|
2
|
+
module Keycloak
|
3
|
+
module API
|
4
|
+
module KeycloakAPIExtensionResources
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include Concerns::APIUtil
|
7
|
+
|
8
|
+
def api_extension_resources_url
|
9
|
+
realm_url + "/keycloak-api-extension/"
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param user_id [String] user id
|
13
|
+
# @return Boolean
|
14
|
+
def if_otp_exists(user_id)
|
15
|
+
url = api_extension_resources_url + "users/#{user_id}/if-otp-exists"
|
16
|
+
JSON.parse(get(url))["status"]
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param user_id [String] user id
|
20
|
+
# @param otp [String] otp
|
21
|
+
# @return Boolean
|
22
|
+
def validate_otp(user_id, otp)
|
23
|
+
url = api_extension_resources_url + "users/#{user_id}/validate-otp"
|
24
|
+
JSON.parse(post(url, otp: otp))["status"]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -5,8 +5,10 @@ module Keycloak
|
|
5
5
|
include Concerns::APIUtil
|
6
6
|
|
7
7
|
# @param realm_rep [Keycloak::Model::RealmRepresentation] realm representation
|
8
|
+
# @return [String] realm id
|
8
9
|
def create_realm(realm_rep)
|
9
|
-
post("#{@auth_server_url}/admin/realms/", realm_rep.to_json, headers: {content_type: :json})
|
10
|
+
res = post("#{@auth_server_url}/admin/realms/", realm_rep.to_json, headers: {content_type: :json})
|
11
|
+
res.headers[:location].split("/")[-1]
|
10
12
|
end
|
11
13
|
|
12
14
|
# @param realm [String] realm name
|
@@ -43,11 +43,37 @@ module Keycloak
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
# @param username [String] username
|
46
47
|
# @return [Keycloak::Model::UserRepresentation] user representation
|
47
48
|
def find_user_by_username(username)
|
48
49
|
find_users({username: username}).to_a[0]
|
49
50
|
end
|
50
51
|
|
52
|
+
# @param role [String] role name
|
53
|
+
# @param params [Hash] extra params
|
54
|
+
# @return [Keycloak::Model::UserRepresentation] user representation
|
55
|
+
def find_user_by_role(role, params = {})
|
56
|
+
url = "#{admin_realm_url}/roles/#{role}/users"
|
57
|
+
Utils::RepresentationIterator.new(self, params) do
|
58
|
+
JSON.parse(get(url, params: params)).map { |user| Model::UserRepresentation.new(user) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param id_of_client [String] id of client (not client-id)
|
63
|
+
# @param role [String] role name
|
64
|
+
# @param params [Hash] extra params
|
65
|
+
# @return [Keycloak::Model::UserRepresentation] user representation
|
66
|
+
def find_user_by_client_role(id_of_client, role, params = {})
|
67
|
+
url = "#{admin_realm_url}/clients/#{id_of_client}/roles/#{role}/users"
|
68
|
+
Utils::RepresentationIterator.new(self, params) do
|
69
|
+
JSON.parse(get(url, params: params)).map { |user| Model::UserRepresentation.new(user) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param id [String] user id
|
74
|
+
def delete_user(id)
|
75
|
+
delete("#{user_resources_url}/#{id}", headers: {content_type: :json})
|
76
|
+
end
|
51
77
|
end
|
52
78
|
end
|
53
79
|
end
|
data/lib/keycloak/client.rb
CHANGED
@@ -8,6 +8,7 @@ module Keycloak
|
|
8
8
|
include API::ClientResources
|
9
9
|
include API::ClientRoleResources
|
10
10
|
include API::RealmResources
|
11
|
+
include API::KeycloakAPIExtensionResources
|
11
12
|
|
12
13
|
attr_reader :auth_server_url, :realm
|
13
14
|
|
@@ -39,7 +40,8 @@ module Keycloak
|
|
39
40
|
username: username,
|
40
41
|
password: password,
|
41
42
|
grant_type: grant_type,
|
42
|
-
client_id: client_id
|
43
|
+
client_id: client_id,
|
44
|
+
scope: "offline_access"
|
43
45
|
}, try_refresh_token: false).body
|
44
46
|
@access_token = res["access_token"]
|
45
47
|
@refresh_token = res["refresh_token"]
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Relay
|
3
|
+
class KeycloakRepresentationIteratorConnection < BaseConnection
|
4
|
+
def cursor_from_node(item)
|
5
|
+
item_index = paged_nodes.index(item)
|
6
|
+
if item_index.nil?
|
7
|
+
raise("Can't generate cursor, item not found in connection: #{item}")
|
8
|
+
else
|
9
|
+
offset = item_index + (paged_nodes_offset || 0)
|
10
|
+
|
11
|
+
encode(offset.to_s)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: support `before`
|
16
|
+
def has_next_page
|
17
|
+
sliced_nodes_count >= (first || max_page_size)
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO: support `before`
|
21
|
+
def has_previous_page
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
return @first if defined? @first
|
27
|
+
|
28
|
+
@first = get_limited_arg(:first)
|
29
|
+
@first = max_page_size if @first && max_page_size && @first > max_page_size
|
30
|
+
@first
|
31
|
+
end
|
32
|
+
|
33
|
+
def last
|
34
|
+
return @last if defined? @last
|
35
|
+
|
36
|
+
@last = get_limited_arg(:last)
|
37
|
+
@last = max_page_size if @last && max_page_size && @last > max_page_size
|
38
|
+
@last
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# apply first / last limit results
|
44
|
+
# @return [Array]
|
45
|
+
def paged_nodes
|
46
|
+
return @paged_nodes if defined? @paged_nodes
|
47
|
+
|
48
|
+
items = sliced_nodes
|
49
|
+
|
50
|
+
if first
|
51
|
+
items.till = items.first + first
|
52
|
+
end
|
53
|
+
|
54
|
+
if max_page_size && !first && !last
|
55
|
+
items.till = items.first + max_page_size
|
56
|
+
end
|
57
|
+
|
58
|
+
@paged_nodes_offset = items.first
|
59
|
+
|
60
|
+
@paged_nodes = items.to_a
|
61
|
+
end
|
62
|
+
|
63
|
+
def paged_nodes_offset
|
64
|
+
paged_nodes && @paged_nodes_offset
|
65
|
+
end
|
66
|
+
|
67
|
+
def relation_offset(iterator)
|
68
|
+
iterator.first
|
69
|
+
end
|
70
|
+
|
71
|
+
def relation_limit(iterator)
|
72
|
+
iterator.till
|
73
|
+
end
|
74
|
+
|
75
|
+
# Apply cursors to edges
|
76
|
+
def sliced_nodes
|
77
|
+
return @sliced_nodes if defined? @sliced_nodes
|
78
|
+
|
79
|
+
@sliced_nodes = nodes
|
80
|
+
|
81
|
+
if after
|
82
|
+
@sliced_nodes.first = offset_from_cursor(after) + 1
|
83
|
+
end
|
84
|
+
|
85
|
+
@sliced_nodes
|
86
|
+
end
|
87
|
+
|
88
|
+
def sliced_nodes_count
|
89
|
+
return @sliced_nodes_count if defined? @sliced_nodes_count
|
90
|
+
|
91
|
+
@sliced_nodes_count = paged_nodes.size
|
92
|
+
end
|
93
|
+
|
94
|
+
def offset_from_cursor(cursor)
|
95
|
+
decode(cursor).to_i
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
BaseConnection.register_connection_implementation(Keycloak::Utils::RepresentationIterator, KeycloakRepresentationIteratorConnection)
|
100
|
+
end
|
101
|
+
end
|
data/lib/keycloak/realm.rb
CHANGED
data/lib/keycloak/user_entity.rb
CHANGED
@@ -2,6 +2,8 @@ module Keycloak
|
|
2
2
|
module UserEntity
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
attr_accessor :keycloak_token
|
6
|
+
|
5
7
|
included do
|
6
8
|
def keycloak_client
|
7
9
|
raise NotImplementedError
|
@@ -20,7 +22,9 @@ module Keycloak
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def has_role?(role)
|
23
|
-
|
25
|
+
keycloak_token ?
|
26
|
+
keycloak_token.has_role?(role) :
|
27
|
+
realm_roles.map(&:name).include?(role)
|
24
28
|
end
|
25
29
|
|
26
30
|
def _set_or_get_keycloak_data(attr_name, reload)
|
data/lib/keycloak/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keycloak-ruby-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fuxin Hao
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jwt
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: json-jwt
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: bundler
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +136,20 @@ dependencies:
|
|
108
136
|
- - ">="
|
109
137
|
- !ruby/object:Gem::Version
|
110
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: factory_bot
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
111
153
|
description: Keycloak ruby client
|
112
154
|
email:
|
113
155
|
- haofxpro@gmail.com
|
@@ -129,6 +171,7 @@ files:
|
|
129
171
|
- lib/keycloak/api.rb
|
130
172
|
- lib/keycloak/api/client_resources.rb
|
131
173
|
- lib/keycloak/api/client_role_resources.rb
|
174
|
+
- lib/keycloak/api/keycloak_api_extension_resources.rb
|
132
175
|
- lib/keycloak/api/protection_resources.rb
|
133
176
|
- lib/keycloak/api/realm_resources.rb
|
134
177
|
- lib/keycloak/api/role_resources.rb
|
@@ -136,6 +179,8 @@ files:
|
|
136
179
|
- lib/keycloak/client.rb
|
137
180
|
- lib/keycloak/concerns.rb
|
138
181
|
- lib/keycloak/concerns/api_util.rb
|
182
|
+
- lib/keycloak/graphql.rb
|
183
|
+
- lib/keycloak/graphql/keycloak_representation_iterator_connection.rb
|
139
184
|
- lib/keycloak/model.rb
|
140
185
|
- lib/keycloak/model/base_representation.rb
|
141
186
|
- lib/keycloak/model/client_representation.rb
|
@@ -174,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
174
219
|
version: '0'
|
175
220
|
requirements: []
|
176
221
|
rubyforge_project:
|
177
|
-
rubygems_version: 2.6.
|
222
|
+
rubygems_version: 2.6.14.3
|
178
223
|
signing_key:
|
179
224
|
specification_version: 4
|
180
225
|
summary: Keycloak ruby client
|