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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5fb941053a36de82d660a51be6d041b432c9342
4
- data.tar.gz: 5c9ffc4b484fed6cdbff04f937b0a96f603e457e
3
+ metadata.gz: '086dfbaf42ef3339dd5860e717be6490f625baa3'
4
+ data.tar.gz: c32725910ba174a2722839bf913b4c2d69f9785d
5
5
  SHA512:
6
- metadata.gz: bb526460464780a4be8667ce19e576d3ff790b169d22edf0502f20d9684bab8ca22a65c71c9aeecd7a9e6534729739420d40917208a93c4f791062062adbaf5f
7
- data.tar.gz: 50e74f7a50372209f4b011076ecb9ceeda1b4898ed718b6e4fdf01b4a54ab8ca5415ae28cc4b92d73f16a1880bdabc85343de35bf7dbac05e1a784b9337c3acc
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
@@ -4,3 +4,4 @@ require 'keycloak/api/user_resources'
4
4
  require 'keycloak/api/client_resources'
5
5
  require 'keycloak/api/client_role_resources'
6
6
  require 'keycloak/api/realm_resources'
7
+ require 'keycloak/api/keycloak_api_extension_resources'
@@ -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
- url = admin_realm_url + "/clients"
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 = admin_realm_url + "/clients/#{id}"
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
@@ -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,3 @@
1
+ require 'graphql'
2
+
3
+ require_relative 'graphql/keycloak_representation_iterator_connection'
@@ -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
@@ -1,3 +1,6 @@
1
+ require 'jwt'
2
+ require 'json-jwt'
3
+
1
4
  module Keycloak
2
5
  class Realm
3
6
  @realms = Concurrent::Map.new
@@ -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
- realm_roles.map(&:name).include?(role)
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)
@@ -1,3 +1,3 @@
1
1
  module Keycloak
2
- VERSION = "0.0.15"
2
+ VERSION = "0.0.20"
3
3
  end
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.15
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: 2019-10-23 00:00:00.000000000 Z
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.11
222
+ rubygems_version: 2.6.14.3
178
223
  signing_key:
179
224
  specification_version: 4
180
225
  summary: Keycloak ruby client