keycloak_ruby 0.1.1 → 0.1.3

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
  SHA256:
3
- metadata.gz: 69b02c30af6136bdddbf2bbd025fb4b9468eb4cbc483367ede0774bdc3c4ba71
4
- data.tar.gz: 6f080dfe0802d3124ca38b09077b4fa9793c8032694919b2eb7bccf008fb745a
3
+ metadata.gz: 9b5e24bb870789d3bfef9291dbb90ad2ad87a33e8786320fd2dbe0c759da7f8c
4
+ data.tar.gz: ceebf27600e81da2c16ffb1b82b6cba7e1a0d0176489c138fbf80ecab0aa3069
5
5
  SHA512:
6
- metadata.gz: bb4794636898be09829f9bf14395db896af56ddcb9ef92738225f95ad23be49135463c9deb0abaa7274d453574818647326cba80c89e306b1098ab5169805746
7
- data.tar.gz: 3450f8d4fdd49e76b05fbd596078d03df83cb3b5d9869f93e8f9525ddac8a8dabaa7c089e55b141d375bfa32209e944615b799ee1a036dd5236c3bc82d20d86b
6
+ metadata.gz: '054193e51f510f205ed667fd34a292ff89761fab3af75b77304d7aafa27680b9f08d0d0ba6d068471d417459812de443587f0689cd0efec9b98dfdecb5ddf109'
7
+ data.tar.gz: d9d7060295a053e533fe105fbb157bc1e0ff267bc2b78d9a17654149816dc76a3cb9fb37314233276c8f7b8f48abedc89ed3a0de36ec745d6a34e95f04c77834
data/.reek.yml ADDED
@@ -0,0 +1,9 @@
1
+ detectors:
2
+ UncommunicativeVariableName:
3
+ accept:
4
+ - e
5
+ TooManyStatements:
6
+ enabled: true
7
+ exclude:
8
+ - initialize
9
+ max_statements: 8
data/.rspec_status CHANGED
@@ -1,68 +1,72 @@
1
1
  example_id | status | run_time |
2
2
  -------------------------------------------------------- | ------ | --------------- |
3
- ./spec/keycloak_ruby/client_spec.rb[1:1:1:1] | passed | 0.01112 seconds |
4
- ./spec/keycloak_ruby/client_spec.rb[1:1:2:1] | passed | 0.00164 seconds |
5
- ./spec/keycloak_ruby/client_spec.rb[1:2:1:1] | passed | 0.00447 seconds |
6
- ./spec/keycloak_ruby/client_spec.rb[1:2:2:1] | passed | 0.00116 seconds |
7
- ./spec/keycloak_ruby/client_spec.rb[1:3:1] | passed | 0.00283 seconds |
8
- ./spec/keycloak_ruby/client_spec.rb[1:3:2] | passed | 0.00209 seconds |
9
- ./spec/keycloak_ruby/client_spec.rb[1:4:1:1] | passed | 0.00114 seconds |
10
- ./spec/keycloak_ruby/client_spec.rb[1:4:2:1] | passed | 0.00102 seconds |
11
- ./spec/keycloak_ruby/client_spec.rb[1:5:1] | passed | 0.00116 seconds |
12
- ./spec/keycloak_ruby/client_spec.rb[1:6:1:1] | passed | 0.00173 seconds |
13
- ./spec/keycloak_ruby/client_spec.rb[1:6:2:1] | passed | 0.00092 seconds |
14
- ./spec/keycloak_ruby/config_spec.rb[1:1:1:1] | passed | 0.00052 seconds |
15
- ./spec/keycloak_ruby/config_spec.rb[1:1:1:2] | passed | 0.00045 seconds |
16
- ./spec/keycloak_ruby/config_spec.rb[1:1:1:3] | passed | 0.00048 seconds |
17
- ./spec/keycloak_ruby/config_spec.rb[1:1:1:4] | passed | 0.00038 seconds |
18
- ./spec/keycloak_ruby/config_spec.rb[1:1:1:5] | passed | 0.00039 seconds |
19
- ./spec/keycloak_ruby/config_spec.rb[1:1:2:1] | passed | 0.00006 seconds |
20
- ./spec/keycloak_ruby/config_spec.rb[1:2:1] | passed | 0.00044 seconds |
21
- ./spec/keycloak_ruby/config_spec.rb[1:2:2] | passed | 0.0004 seconds |
22
- ./spec/keycloak_ruby/config_spec.rb[1:2:3] | passed | 0.00026 seconds |
23
- ./spec/keycloak_ruby/config_spec.rb[1:2:4] | passed | 0.00025 seconds |
24
- ./spec/keycloak_ruby/config_spec.rb[1:3:1] | passed | 0.00024 seconds |
25
- ./spec/keycloak_ruby/config_spec.rb[1:3:2] | passed | 0.00024 seconds |
26
- ./spec/keycloak_ruby/config_spec.rb[1:3:3] | passed | 0.00027 seconds |
27
- ./spec/keycloak_ruby/config_spec.rb[1:3:4] | passed | 0.00024 seconds |
28
- ./spec/keycloak_ruby/config_spec.rb[1:4:1:1] | passed | 0.00379 seconds |
29
- ./spec/keycloak_ruby/config_spec.rb[1:4:2:1] | passed | 0.00042 seconds |
30
- ./spec/keycloak_ruby/config_spec.rb[1:4:2:2] | passed | 0.00029 seconds |
31
- ./spec/keycloak_ruby/request_performer_spec.rb[1:1:1:1] | passed | 0.00099 seconds |
32
- ./spec/keycloak_ruby/request_performer_spec.rb[1:1:1:2] | passed | 0.0008 seconds |
33
- ./spec/keycloak_ruby/request_performer_spec.rb[1:1:2:1] | passed | 0.00067 seconds |
34
- ./spec/keycloak_ruby/request_performer_spec.rb[1:1:3:1] | passed | 0.00058 seconds |
35
- ./spec/keycloak_ruby/request_performer_spec.rb[1:1:4:1] | passed | 0.0006 seconds |
36
- ./spec/keycloak_ruby/response_validator_spec.rb[1:1:1:1] | passed | 0.00065 seconds |
37
- ./spec/keycloak_ruby/response_validator_spec.rb[1:1:2:1] | passed | 0.00017 seconds |
38
- ./spec/keycloak_ruby/response_validator_spec.rb[1:1:3:1] | passed | 0.00021 seconds |
39
- ./spec/keycloak_ruby/response_validator_spec.rb[1:1:4:1] | passed | 0.00009 seconds |
40
- ./spec/keycloak_ruby/response_validator_spec.rb[1:1:5:1] | passed | 0.00008 seconds |
41
- ./spec/keycloak_ruby/response_validator_spec.rb[1:1:6:1] | passed | 0.00011 seconds |
42
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:1:1] | passed | 0.0001 seconds |
43
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:2:1] | passed | 0.00012 seconds |
44
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:3:1] | passed | 0.0001 seconds |
45
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:4:1] | passed | 0.00009 seconds |
46
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:5:1] | passed | 0.00011 seconds |
47
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:6:1] | passed | 0.00016 seconds |
48
- ./spec/keycloak_ruby/response_validator_spec.rb[1:2:7:1] | passed | 0.00011 seconds |
49
- ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:1:1] | passed | 0.00085 seconds |
50
- ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:1:2] | passed | 0.00077 seconds |
51
- ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:2:1] | passed | 0.00093 seconds |
52
- ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:3:1] | passed | 0.00056 seconds |
53
- ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:4:1] | passed | 0.00072 seconds |
54
- ./spec/keycloak_ruby/token_service_spec.rb[1:1:1:1] | passed | 0.00696 seconds |
55
- ./spec/keycloak_ruby/token_service_spec.rb[1:1:2:1] | passed | 0.00129 seconds |
56
- ./spec/keycloak_ruby/token_service_spec.rb[1:1:2:2] | passed | 0.00826 seconds |
57
- ./spec/keycloak_ruby/token_service_spec.rb[1:2:1] | passed | 0.00072 seconds |
58
- ./spec/keycloak_ruby/token_service_spec.rb[1:2:2] | passed | 0.00044 seconds |
59
- ./spec/keycloak_ruby/token_service_spec.rb[1:2:3] | passed | 0.0004 seconds |
60
- ./spec/keycloak_ruby/token_service_spec.rb[1:3:1] | passed | 0.00056 seconds |
61
- ./spec/keycloak_ruby/token_service_spec.rb[1:3:2] | passed | 0.00083 seconds |
62
- ./spec/keycloak_ruby/token_service_spec.rb[1:3:3] | passed | 0.00038 seconds |
63
- ./spec/keycloak_ruby/token_service_spec.rb[1:4:1:1] | passed | 0.00057 seconds |
64
- ./spec/keycloak_ruby/token_service_spec.rb[1:4:2:1] | passed | 0.00072 seconds |
65
- ./spec/keycloak_ruby/token_service_spec.rb[1:4:3:1] | passed | 0.00056 seconds |
66
- ./spec/keycloak_ruby/token_service_spec.rb[1:4:3:2] | passed | 0.00055 seconds |
67
- ./spec/keycloak_ruby/token_service_spec.rb[1:5:1] | passed | 0.0007 seconds |
68
- ./spec/keycloak_ruby_spec.rb[1:1] | passed | 0.00005 seconds |
3
+ ./spec/keycloak_ruby/client_spec.rb[1:1:1:1] | passed | 0.0256 seconds |
4
+ ./spec/keycloak_ruby/client_spec.rb[1:1:2:1] | passed | 0.00494 seconds |
5
+ ./spec/keycloak_ruby/client_spec.rb[1:2:1:1] | passed | 0.01574 seconds |
6
+ ./spec/keycloak_ruby/client_spec.rb[1:2:2:1] | passed | 0.00294 seconds |
7
+ ./spec/keycloak_ruby/client_spec.rb[1:3:1] | passed | 0.00867 seconds |
8
+ ./spec/keycloak_ruby/client_spec.rb[1:3:2] | passed | 0.00564 seconds |
9
+ ./spec/keycloak_ruby/client_spec.rb[1:4:1:1] | passed | 0.00271 seconds |
10
+ ./spec/keycloak_ruby/client_spec.rb[1:4:2:1] | passed | 0.00267 seconds |
11
+ ./spec/keycloak_ruby/client_spec.rb[1:5:1] | passed | 0.00413 seconds |
12
+ ./spec/keycloak_ruby/client_spec.rb[1:6:1:1] | passed | 0.00466 seconds |
13
+ ./spec/keycloak_ruby/client_spec.rb[1:6:2:1] | passed | 0.00228 seconds |
14
+ ./spec/keycloak_ruby/config_spec.rb[1:1:1:1] | passed | 0.00122 seconds |
15
+ ./spec/keycloak_ruby/config_spec.rb[1:1:1:2] | passed | 0.00104 seconds |
16
+ ./spec/keycloak_ruby/config_spec.rb[1:1:1:3] | passed | 0.0009 seconds |
17
+ ./spec/keycloak_ruby/config_spec.rb[1:1:1:4] | passed | 0.00113 seconds |
18
+ ./spec/keycloak_ruby/config_spec.rb[1:1:1:5] | passed | 0.00111 seconds |
19
+ ./spec/keycloak_ruby/config_spec.rb[1:1:2:1] | passed | 0.00025 seconds |
20
+ ./spec/keycloak_ruby/config_spec.rb[1:2:1] | passed | 0.00117 seconds |
21
+ ./spec/keycloak_ruby/config_spec.rb[1:2:2] | passed | 0.00122 seconds |
22
+ ./spec/keycloak_ruby/config_spec.rb[1:2:3] | passed | 0.00097 seconds |
23
+ ./spec/keycloak_ruby/config_spec.rb[1:2:4] | passed | 0.00092 seconds |
24
+ ./spec/keycloak_ruby/config_spec.rb[1:3:1] | passed | 0.00076 seconds |
25
+ ./spec/keycloak_ruby/config_spec.rb[1:3:2] | passed | 0.00078 seconds |
26
+ ./spec/keycloak_ruby/config_spec.rb[1:3:3] | passed | 0.00059 seconds |
27
+ ./spec/keycloak_ruby/config_spec.rb[1:3:4] | passed | 0.00073 seconds |
28
+ ./spec/keycloak_ruby/config_spec.rb[1:4:1:1] | passed | 0.01004 seconds |
29
+ ./spec/keycloak_ruby/config_spec.rb[1:4:2:1] | passed | 0.0012 seconds |
30
+ ./spec/keycloak_ruby/config_spec.rb[1:4:2:2] | passed | 0.00094 seconds |
31
+ ./spec/keycloak_ruby/request_performer_spec.rb[1:1:1:1] | passed | 0.00226 seconds |
32
+ ./spec/keycloak_ruby/request_performer_spec.rb[1:1:1:2] | passed | 0.00168 seconds |
33
+ ./spec/keycloak_ruby/request_performer_spec.rb[1:1:2:1] | passed | 0.00181 seconds |
34
+ ./spec/keycloak_ruby/request_performer_spec.rb[1:1:3:1] | passed | 0.00152 seconds |
35
+ ./spec/keycloak_ruby/request_performer_spec.rb[1:1:4:1] | passed | 0.00155 seconds |
36
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:1:1:1] | passed | 0.00203 seconds |
37
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:1:2:1] | passed | 0.00056 seconds |
38
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:1:3:1] | passed | 0.00053 seconds |
39
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:1:4:1] | passed | 0.00034 seconds |
40
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:1:5:1] | passed | 0.00028 seconds |
41
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:1:6:1] | passed | 0.00033 seconds |
42
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:1:1] | passed | 0.00032 seconds |
43
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:2:1] | passed | 0.00042 seconds |
44
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:3:1] | passed | 0.00034 seconds |
45
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:4:1] | passed | 0.00044 seconds |
46
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:5:1] | passed | 0.00042 seconds |
47
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:6:1] | passed | 0.00035 seconds |
48
+ ./spec/keycloak_ruby/response_validator_spec.rb[1:2:7:1] | passed | 0.00032 seconds |
49
+ ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:1:1] | passed | 0.00247 seconds |
50
+ ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:1:2] | passed | 0.00199 seconds |
51
+ ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:2:1] | passed | 0.00237 seconds |
52
+ ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:3:1] | passed | 0.0017 seconds |
53
+ ./spec/keycloak_ruby/token_refresher_spec.rb[1:1:4:1] | passed | 0.0022 seconds |
54
+ ./spec/keycloak_ruby/token_service_spec.rb[1:1:1:1] | passed | 0.01982 seconds |
55
+ ./spec/keycloak_ruby/token_service_spec.rb[1:1:2:1] | passed | 0.00324 seconds |
56
+ ./spec/keycloak_ruby/token_service_spec.rb[1:1:2:2] | passed | 0.0209 seconds |
57
+ ./spec/keycloak_ruby/token_service_spec.rb[1:2:1] | passed | 0.00223 seconds |
58
+ ./spec/keycloak_ruby/token_service_spec.rb[1:2:2] | passed | 0.00208 seconds |
59
+ ./spec/keycloak_ruby/token_service_spec.rb[1:2:3] | passed | 0.00168 seconds |
60
+ ./spec/keycloak_ruby/token_service_spec.rb[1:3:1] | passed | 0.00144 seconds |
61
+ ./spec/keycloak_ruby/token_service_spec.rb[1:3:2] | passed | 0.00124 seconds |
62
+ ./spec/keycloak_ruby/token_service_spec.rb[1:3:3] | passed | 0.0013 seconds |
63
+ ./spec/keycloak_ruby/token_service_spec.rb[1:4:1:1] | passed | 0.00157 seconds |
64
+ ./spec/keycloak_ruby/token_service_spec.rb[1:4:2:1] | passed | 0.00238 seconds |
65
+ ./spec/keycloak_ruby/token_service_spec.rb[1:4:3:1] | passed | 0.00333 seconds |
66
+ ./spec/keycloak_ruby/token_service_spec.rb[1:4:3:2] | passed | 0.0019 seconds |
67
+ ./spec/keycloak_ruby/token_service_spec.rb[1:5:1] | passed | 0.00224 seconds |
68
+ ./spec/keycloak_ruby_spec.rb[1:1] | passed | 0.00021 seconds |
69
+ ./spec/keycloak_ruby_spec.rb[1:2] | passed | 0.01876 seconds |
70
+ ./spec/keycloak_ruby_spec.rb[1:3:1] | passed | 0.00099 seconds |
71
+ ./spec/keycloak_ruby_spec.rb[1:3:2] | passed | 0.00095 seconds |
72
+ ./spec/keycloak_ruby_spec.rb[1:3:3] | passed | 0.00024 seconds |
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-3.4.1
data/README.md CHANGED
@@ -7,6 +7,9 @@ This library is designed to use Keycloak identification in Rails application
7
7
  Add to Gemfile
8
8
 
9
9
  ```bash
10
+ # stable version
11
+ gem "keycloak_ruby"
12
+ # latest master
10
13
  gem "keycloak_ruby", git: "https://github.com/sergey-arkhipov/keycloak_ruby.git"
11
14
 
12
15
  ```
@@ -17,7 +20,12 @@ Create initializer for omniauth manually or use generator (config/initializers/o
17
20
  rails generate keycloak_ruby:install
18
21
  ```
19
22
 
20
- Now under active development, so you need create manually:
23
+ Now under active development, so you need create controller and routes manually:
24
+
25
+ - include `KeycloakRuby::Authentication` in ApplicationController
26
+ - create `SeesionController`
27
+ - add routes to `routes.rb
28
+ `
21
29
 
22
30
  ```ruby
23
31
  # ApplicationController
@@ -54,7 +62,29 @@ delete '/logout', to: 'sessions#destroy', as: :logout
54
62
 
55
63
  ```
56
64
 
57
- It is assumed that you have a User model in Rails app
65
+ It is assumed that you have a `User` model in Rails app
66
+
67
+ ### Configuration Warning
68
+
69
+ When using `ENV[...]` or `ENV.fetch(...)` inside your `config/keycloak.yml`, make sure the referenced environment variables are actually defined.
70
+
71
+ If any variable is missing, the application may crash at boot time with one of the following exceptions:
72
+
73
+ - `KeyError` - when using `ENV.fetch(...)` without a fallback default;
74
+ - `KeycloakRuby::Errors::ConfigurationError` - when a required value is `nil` or blank after config evaluation.
75
+
76
+ This issue can happen when building Docker images. To avoid this, **use safe defaults** via `ENV.fetch('VAR', 'fallback')`:
77
+
78
+ ```yaml
79
+ production:
80
+ keycloak_url: <%= ENV.fetch('KEYCLOAK_URL', 'https://keycloak.example.com') %>
81
+ app_host: <%= ENV.fetch('DEFAULT_APP_HOST', 'https://app.example.com') %>
82
+ realm: <%= ENV.fetch('KEYCLOAK_REALM', 'my-realm') %>
83
+ admin_client_id: <%= ENV.fetch('KEYCLOAK_ADMIN_CLIENT_ID', 'admin-cli') %>
84
+ admin_client_secret: <%= ENV.fetch('KEYCLOAK_ADMIN_CLIENT_SECRET', 'changeme') %>
85
+ oauth_client_id: <%= ENV.fetch('KEYCLOAK_OAUTH_CLIENT_ID', 'my-client') %>
86
+ oauth_client_secret: <%= ENV.fetch('KEYCLOAK_OAUTH_CLIENT_SECRET', 'secret') %>
87
+ ```
58
88
 
59
89
  ## Architecture Overview
60
90
 
@@ -145,13 +175,9 @@ sequenceDiagram
145
175
 
146
176
  ```
147
177
 
148
- ## Test
178
+ ## Testing in application
149
179
 
150
180
  For test purpose there mock helper sign_in(user)
151
181
 
152
182
  - add `require "keycloak_ruby/testing/keycloak_helpers"` to keycloak_helper.rb
153
183
  - add sign_in in tests `config.include KeycloakRuby::Testing::KeycloakHelpers`
154
-
155
- ```
156
-
157
- ```
data/Rakefile CHANGED
@@ -8,7 +8,9 @@ RSpec::Core::RakeTask.new(:spec) do |task|
8
8
  end
9
9
 
10
10
  require "rubocop/rake_task"
11
-
12
11
  RuboCop::RakeTask.new
13
12
 
14
- task default: %i[spec rubocop]
13
+ require "reek/rake/task"
14
+ Reek::Rake::Task.new
15
+
16
+ task default: %i[spec rubocop reek]
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # lib/keycloak_ruby/authentication.rb
4
+ require "active_support/concern"
4
5
  module KeycloakRuby
5
6
  # Concern to add methods to ApplicationController
6
7
  module Authentication
@@ -8,6 +9,8 @@ module KeycloakRuby
8
9
 
9
10
  included do
10
11
  before_action :authenticate_user!
12
+
13
+ helper_method :current_user, :user_signed_in?
11
14
  end
12
15
 
13
16
  def keycloak_jwt_service
@@ -21,5 +24,9 @@ module KeycloakRuby
21
24
  def current_user
22
25
  @current_user ||= keycloak_jwt_service.find_user
23
26
  end
27
+
28
+ def user_signed_in?
29
+ current_user.present?
30
+ end
24
31
  end
25
32
  end
@@ -44,7 +44,7 @@ module KeycloakRuby
44
44
  # @raise [KeycloakRuby::Errors::UserCreationError] If user creation fails.
45
45
  # @return [Hash] The newly created user's data.
46
46
  def create_user(user_attrs = {})
47
- user_data = build_user_data(user_attrs)
47
+ user_data = self.class.build_user_data(user_attrs)
48
48
 
49
49
  response = http_request(
50
50
  http_method: :post,
@@ -112,19 +112,7 @@ module KeycloakRuby
112
112
  update_redirect_uris_for(client_record["id"], redirect_uris)
113
113
  end
114
114
 
115
- private
116
-
117
- def build_auth_body(username, password)
118
- {
119
- client_id: @config.oauth_client_id,
120
- client_secret: @config.oauth_client_secret,
121
- username: username,
122
- password: password,
123
- grant_type: "password"
124
- }
125
- end
126
-
127
- def build_user_data(attrs)
115
+ def self.build_user_data(attrs)
128
116
  { username: attrs.fetch(:username), email: attrs.fetch(:email), enabled: true,
129
117
  credentials: [
130
118
  {
@@ -135,13 +123,7 @@ module KeycloakRuby
135
123
  ] }
136
124
  end
137
125
 
138
- # Builds RequestParams and passes them to the RequestPerformer.
139
- def http_request(options = {})
140
- params = build_request_params(options)
141
- @request_performer.call(params)
142
- end
143
-
144
- def build_request_params(opts)
126
+ def self.build_request_params(opts)
145
127
  RequestParams.new(
146
128
  http_method: opts.fetch(:http_method),
147
129
  url: opts.fetch(:url),
@@ -153,6 +135,24 @@ module KeycloakRuby
153
135
  )
154
136
  end
155
137
 
138
+ private
139
+
140
+ def build_auth_body(username, password)
141
+ {
142
+ client_id: @config.oauth_client_id,
143
+ client_secret: @config.oauth_client_secret,
144
+ username: username,
145
+ password: password,
146
+ grant_type: "password"
147
+ }
148
+ end
149
+
150
+ # Builds RequestParams and passes them to the RequestPerformer.
151
+ def http_request(options = {})
152
+ params = self.class.build_request_params(options)
153
+ @request_performer.call(params)
154
+ end
155
+
156
156
  # Retrieves client details by its "clientId".
157
157
  #
158
158
  # @param client_id [String] The "clientId" in Keycloak.
@@ -39,6 +39,10 @@ module KeycloakRuby
39
39
  oauth_client_id
40
40
  oauth_client_secret
41
41
  ].freeze
42
+
43
+ # Default environment
44
+ DEFAULT_ENV = "development"
45
+
42
46
  # :reek:Attribute
43
47
  attr_accessor :keycloak_url,
44
48
  :app_host,
@@ -91,7 +95,6 @@ module KeycloakRuby
91
95
  private
92
96
 
93
97
  # Loads configuration from YAML file if it exists
94
- # :reek:ManualDispatch
95
98
  def load_config
96
99
  return unless File.exist?(@config_path)
97
100
 
@@ -102,16 +105,31 @@ module KeycloakRuby
102
105
 
103
106
  def load_yaml_file
104
107
  YAML.safe_load(ERB.new(File.read(@config_path)).result, aliases: true)
108
+ rescue Errno::ENOENT, Psych::SyntaxError => e
109
+ raise Errors::ConfigurationError, "Failed to load YAML from #{@config_path}: #{e.message}"
105
110
  end
106
111
 
112
+ # Determines current environment
113
+ #
114
+ # @return [String] Current environment name
115
+ # @api private
107
116
  def current_env
108
- defined?(Rails) ? Rails.env : ENV["APP_ENV"] || "development"
117
+ @current_env ||= (defined?(Rails) && Rails.env) || ENV["APP_ENV"] || DEFAULT_ENV
109
118
  end
110
119
 
120
+ # Applies configuration from hash
121
+ #
122
+ # @param config_hash [Hash] Configuration key-value pairs
123
+ # @api private
111
124
  def apply_config(config_hash)
112
125
  config_hash.each do |key, value|
113
126
  setter = :"#{key}="
114
- public_send(setter, value) if respond_to?(setter)
127
+ begin
128
+ public_send(setter, value)
129
+ rescue NoMethodError
130
+ # Silently ignore unknown configuration keys
131
+ next
132
+ end
115
133
  end
116
134
  end
117
135
  end
@@ -21,27 +21,21 @@ module KeycloakRuby
21
21
  # @raise [request_params.error_class] If the response code is not in success_codes
22
22
  # or HTTParty raises an error.
23
23
  def call(request_params)
24
- # To reduce FeatureEnvy, extract local variables
25
- http_method = request_params.http_method
26
- url = request_params.url
27
- headers = request_params.headers
28
- body = request_params.body
29
-
30
- response = HTTParty.send(http_method, url, headers: headers, body: body)
24
+ request_options = request_params.to_h.slice(:headers, :body)
25
+ response = HTTParty.send(request_params.http_method, request_params.url, **request_options)
31
26
  verify_response!(response, request_params)
32
27
  response
33
28
  rescue HTTParty::Error => e
34
- KeycloakRuby.logger.error("#{request_params.error_message} (HTTParty error): #{e.message}")
35
- raise request_params.error_class, e.message
29
+ message = e.message
30
+ KeycloakRuby.logger.error("#{request_params.error_message} (HTTParty error): #{message}")
31
+ raise request_params.error_class, message
36
32
  end
37
33
 
38
34
  private
39
35
 
40
36
  # Safe validation: returns true/false
41
- def verify_response(response, request_params)
42
- code = response.code
43
- success_codes = request_params.success_codes
44
-
37
+ # :reek:UtilityFunction
38
+ def verify_response(code, success_codes)
45
39
  case success_codes
46
40
  when Range
47
41
  success_codes.cover?(code)
@@ -54,12 +48,13 @@ module KeycloakRuby
54
48
 
55
49
  # Bang version that raises an error on invalid response
56
50
  def verify_response!(response, request_params)
57
- return if verify_response(response, request_params)
51
+ code = response.code
52
+ return if verify_response(code, request_params.success_codes)
58
53
 
59
- code = response.code
60
- error_message = request_params.error_message
61
- KeycloakRuby.logger.error("#{error_message}: #{code} => #{response.body}")
62
- raise request_params.error_class, "#{error_message}: #{code} => #{response.body}"
54
+ message = request_params.error_message
55
+ body = response.body
56
+ KeycloakRuby.logger.error("#{message}: #{code} => #{body}")
57
+ raise request_params.error_class, "#{message}: #{code} => #{body}"
63
58
  end
64
59
  end
65
60
  end
@@ -85,15 +85,16 @@ module KeycloakRuby
85
85
  # Raises appropriate validation error based on failure reason
86
86
  # @raise [KeycloakRuby::Errors::TokenRefreshFailed]
87
87
  def raise_validation_error # rubocop:disable Metrics/MethodLength
88
+ error_description = @data["error_description"]
88
89
  if !valid_http_status?
89
90
  raise Errors::TokenRefreshFailed,
90
91
  "Keycloak API request failed with status #{@response.code}: #{extract_error_message}"
91
92
  elsif invalid_grant?
92
93
  raise Errors::TokenRefreshFailed,
93
- "Invalid grant: #{@data["error_description"] || "Refresh token invalid or expired"}"
94
+ "Invalid grant: #{error_description || "Refresh token invalid or expired"}"
94
95
  elsif error_present?
95
96
  raise Errors::TokenRefreshFailed,
96
- "Keycloak error: #{@data["error"]} - #{@data["error_description"]}"
97
+ "Keycloak error: #{@data["error"]} - #{error_description}"
97
98
  else
98
99
  raise Errors::TokenRefreshFailed,
99
100
  "Invalid response: access token missing from response"
@@ -103,10 +104,12 @@ module KeycloakRuby
103
104
  # Extracts error message from response
104
105
  # @return [String]
105
106
  def extract_error_message
106
- if @data["error_description"]
107
- @data["error_description"]
108
- elsif @response.body.length < 100 # Prevent huge error messages
109
- @response.body
107
+ error_description = @data["error_description"]
108
+ response_body = @response.body
109
+ if error_description
110
+ error_description
111
+ elsif response_body.length < 100 # Prevent huge error messages
112
+ response_body
110
113
  else
111
114
  "See response body for details"
112
115
  end
@@ -56,32 +56,31 @@ module KeycloakRuby
56
56
  private
57
57
 
58
58
  def mock_keycloak_login(user, use_capybara: true)
59
- OmniAuth.config.test_mode = true
59
+ config = OmniAuth.config
60
+ config.test_mode = true
60
61
  token_data = generate_fake_tokens(user)
61
- OmniAuth.config.mock_auth[:keycloak] = token_data
62
+ config.mock_auth[:keycloak] = token_data
62
63
 
63
- if use_capybara
64
- capybara_login
65
- else
66
- store_session(token_data.credentials)
67
- end
64
+ use_capybara ? capybara_login : store_session(token_data.credentials)
68
65
  end
69
66
 
70
67
  def capybara_login
71
68
  visit "/login"
72
- click_on I18n.t("user.login") if page.has_button? I18n.t("user.login")
69
+ translated_login_link = I18n.t("user.login")
70
+ click_on translated_login_link if page.has_button? translated_login_link
73
71
  end
74
72
 
75
73
  def generate_fake_tokens(user)
76
74
  email = user.email
77
- token_payload = { "email" => email, "exp" => 2.hours.from_now.to_i }
75
+ expired_time = 2.hours.from_now.to_i
76
+ token_payload = { "email" => email, "exp" => expired_time }
78
77
 
79
78
  OmniAuth::AuthHash.new(provider: "keycloak", uid: "uid-#{email}", info: { email: email },
80
79
  credentials: OmniAuth::AuthHash.new(
81
80
  token: JWT.encode(token_payload, nil, "none"),
82
81
  refresh_token: "fake-refresh-#{email}",
83
82
  id_token: "fake-id-#{email}",
84
- expires_at: 2.hours.from_now.to_i
83
+ expires_at: expired_time
85
84
  ))
86
85
  end
87
86
 
@@ -118,6 +117,7 @@ if defined?(RSpec)
118
117
  end
119
118
  elsif defined?(Minitest)
120
119
  module Minitest
120
+ # Include helpers in minitest module
121
121
  class Test
122
122
  include KeycloakRuby::Testing::KeycloakHelpers
123
123
  end
@@ -88,7 +88,7 @@ module KeycloakRuby
88
88
  {
89
89
  "Content-Type" => "application/x-www-form-urlencoded",
90
90
  "Accept" => "application/json",
91
- "User-Agent" => "KeycloakRuby/#{KeycloakRuby::Version::VERSION}"
91
+ "User-Agent" => "KeycloakRuby/#{KeycloakRuby::VERSION}"
92
92
  }
93
93
  end
94
94
 
@@ -20,8 +20,12 @@ module KeycloakRuby
20
20
  end
21
21
 
22
22
  # Store token
23
+ # :reek:DuplicateMethodCall
24
+ # :reek:FeatureEnvy
23
25
  def store_tokens(data)
24
- @session[:access_token] = extract_access_token(data)
26
+ # It's necessary, because omniauth return request.env["omniauth.auth"] as 'token', not 'access_token'
27
+
28
+ @session[:access_token] = data["token"] || data["access_token"]
25
29
  @session[:refresh_token] = data["refresh_token"] if data["refresh_token"]
26
30
  @session[:id_token] = data["id_token"] if data["id_token"]
27
31
  end
@@ -33,9 +37,6 @@ module KeycloakRuby
33
37
  private
34
38
 
35
39
  # It's necessary, because omniauth return request.env["omniauth.auth"] as 'token', not 'access_token'
36
- def extract_access_token(data)
37
- data["token"] || data["access_token"]
38
- end
39
40
 
40
41
  # Gets current token or attempts refresh if expired
41
42
  # @return [Hash, nil] Decoded token claims
@@ -52,6 +53,7 @@ module KeycloakRuby
52
53
 
53
54
  # Decodes JWT token
54
55
  # @raise [Errors::TokenExpired, Errors::TokenInvalid]
56
+ # :reek:DuplicateMethodCall
55
57
  def decode_token(token)
56
58
  options = jwt_decode_options
57
59
 
@@ -57,8 +57,9 @@ module KeycloakRuby
57
57
  #
58
58
  # @raise [KeycloakRuby::Errors::Error] if any request to Keycloak fails
59
59
  def find_or_create(user_attrs = {})
60
- users = find(user_attrs[:email])
61
- user = users.detect { |u| u["email"].casecmp?(user_attrs[:email]) }
60
+ user_email = user_attrs[:email]
61
+ users = find(user_email)
62
+ user = users.detect { |user| user["email"].casecmp?(user_email) }
62
63
 
63
64
  if user
64
65
  { user_data: user, created: false }