keycloak-ruby-client 0.0.15 → 0.0.20

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