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 +4 -4
- data/.reek.yml +9 -0
- data/.rspec_status +70 -66
- data/.ruby-version +1 -0
- data/README.md +33 -7
- data/Rakefile +4 -2
- data/lib/keycloak_ruby/authentication.rb +7 -0
- data/lib/keycloak_ruby/client.rb +21 -21
- data/lib/keycloak_ruby/config.rb +21 -3
- data/lib/keycloak_ruby/request_performer.rb +13 -18
- data/lib/keycloak_ruby/response_validator.rb +9 -6
- data/lib/keycloak_ruby/testing/keycloak_helpers.rb +10 -10
- data/lib/keycloak_ruby/token_refresher.rb +1 -1
- data/lib/keycloak_ruby/token_service.rb +6 -4
- data/lib/keycloak_ruby/user.rb +3 -2
- data/lib/keycloak_ruby/version.rb +33 -39
- data/lib/keycloak_ruby.rb +37 -20
- data/sig/keycloak_ruby.rbs +17 -634
- metadata +48 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b5e24bb870789d3bfef9291dbb90ad2ad87a33e8786320fd2dbe0c759da7f8c
|
4
|
+
data.tar.gz: ceebf27600e81da2c16ffb1b82b6cba7e1a0d0176489c138fbf80ecab0aa3069
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '054193e51f510f205ed667fd34a292ff89761fab3af75b77304d7aafa27680b9f08d0d0ba6d068471d417459812de443587f0689cd0efec9b98dfdecb5ddf109'
|
7
|
+
data.tar.gz: d9d7060295a053e533fe105fbb157bc1e0ff267bc2b78d9a17654149816dc76a3cb9fb37314233276c8f7b8f48abedc89ed3a0de36ec745d6a34e95f04c77834
|
data/.reek.yml
ADDED
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.
|
4
|
-
./spec/keycloak_ruby/client_spec.rb[1:1:2:1] | passed | 0.
|
5
|
-
./spec/keycloak_ruby/client_spec.rb[1:2:1:1] | passed | 0.
|
6
|
-
./spec/keycloak_ruby/client_spec.rb[1:2:2:1] | passed | 0.
|
7
|
-
./spec/keycloak_ruby/client_spec.rb[1:3:1] | passed | 0.
|
8
|
-
./spec/keycloak_ruby/client_spec.rb[1:3:2] | passed | 0.
|
9
|
-
./spec/keycloak_ruby/client_spec.rb[1:4:1:1] | passed | 0.
|
10
|
-
./spec/keycloak_ruby/client_spec.rb[1:4:2:1] | passed | 0.
|
11
|
-
./spec/keycloak_ruby/client_spec.rb[1:5:1] | passed | 0.
|
12
|
-
./spec/keycloak_ruby/client_spec.rb[1:6:1:1] | passed | 0.
|
13
|
-
./spec/keycloak_ruby/client_spec.rb[1:6:2:1] | passed | 0.
|
14
|
-
./spec/keycloak_ruby/config_spec.rb[1:1:1:1] | passed | 0.
|
15
|
-
./spec/keycloak_ruby/config_spec.rb[1:1:1:2] | passed | 0.
|
16
|
-
./spec/keycloak_ruby/config_spec.rb[1:1:1:3] | passed | 0.
|
17
|
-
./spec/keycloak_ruby/config_spec.rb[1:1:1:4] | passed | 0.
|
18
|
-
./spec/keycloak_ruby/config_spec.rb[1:1:1:5] | passed | 0.
|
19
|
-
./spec/keycloak_ruby/config_spec.rb[1:1:2:1] | passed | 0.
|
20
|
-
./spec/keycloak_ruby/config_spec.rb[1:2:1] | passed | 0.
|
21
|
-
./spec/keycloak_ruby/config_spec.rb[1:2:2] | passed | 0.
|
22
|
-
./spec/keycloak_ruby/config_spec.rb[1:2:3] | passed | 0.
|
23
|
-
./spec/keycloak_ruby/config_spec.rb[1:2:4] | passed | 0.
|
24
|
-
./spec/keycloak_ruby/config_spec.rb[1:3:1] | passed | 0.
|
25
|
-
./spec/keycloak_ruby/config_spec.rb[1:3:2] | passed | 0.
|
26
|
-
./spec/keycloak_ruby/config_spec.rb[1:3:3] | passed | 0.
|
27
|
-
./spec/keycloak_ruby/config_spec.rb[1:3:4] | passed | 0.
|
28
|
-
./spec/keycloak_ruby/config_spec.rb[1:4:1:1] | passed | 0.
|
29
|
-
./spec/keycloak_ruby/config_spec.rb[1:4:2:1] | passed | 0.
|
30
|
-
./spec/keycloak_ruby/config_spec.rb[1:4:2:2] | passed | 0.
|
31
|
-
./spec/keycloak_ruby/request_performer_spec.rb[1:1:1:1] | passed | 0.
|
32
|
-
./spec/keycloak_ruby/request_performer_spec.rb[1:1:1:2] | passed | 0.
|
33
|
-
./spec/keycloak_ruby/request_performer_spec.rb[1:1:2:1] | passed | 0.
|
34
|
-
./spec/keycloak_ruby/request_performer_spec.rb[1:1:3:1] | passed | 0.
|
35
|
-
./spec/keycloak_ruby/request_performer_spec.rb[1:1:4:1] | passed | 0.
|
36
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:1:1:1] | passed | 0.
|
37
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:1:2:1] | passed | 0.
|
38
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:1:3:1] | passed | 0.
|
39
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:1:4:1] | passed | 0.
|
40
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:1:5:1] | passed | 0.
|
41
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:1:6:1] | passed | 0.
|
42
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:1:1] | passed | 0.
|
43
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:2:1] | passed | 0.
|
44
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:3:1] | passed | 0.
|
45
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:4:1] | passed | 0.
|
46
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:5:1] | passed | 0.
|
47
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:6:1] | passed | 0.
|
48
|
-
./spec/keycloak_ruby/response_validator_spec.rb[1:2:7:1] | passed | 0.
|
49
|
-
./spec/keycloak_ruby/token_refresher_spec.rb[1:1:1:1] | passed | 0.
|
50
|
-
./spec/keycloak_ruby/token_refresher_spec.rb[1:1:1:2] | passed | 0.
|
51
|
-
./spec/keycloak_ruby/token_refresher_spec.rb[1:1:2:1] | passed | 0.
|
52
|
-
./spec/keycloak_ruby/token_refresher_spec.rb[1:1:3:1] | passed | 0.
|
53
|
-
./spec/keycloak_ruby/token_refresher_spec.rb[1:1:4:1] | passed | 0.
|
54
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:1:1:1] | passed | 0.
|
55
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:1:2:1] | passed | 0.
|
56
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:1:2:2] | passed | 0.
|
57
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:2:1] | passed | 0.
|
58
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:2:2] | passed | 0.
|
59
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:2:3] | passed | 0.
|
60
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:3:1] | passed | 0.
|
61
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:3:2] | passed | 0.
|
62
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:3:3] | passed | 0.
|
63
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:4:1:1] | passed | 0.
|
64
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:4:2:1] | passed | 0.
|
65
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:4:3:1] | passed | 0.
|
66
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:4:3:2] | passed | 0.
|
67
|
-
./spec/keycloak_ruby/token_service_spec.rb[1:5:1] | passed | 0.
|
68
|
-
./spec/keycloak_ruby_spec.rb[1:1] | passed | 0.
|
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
|
-
##
|
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
@@ -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
|
data/lib/keycloak_ruby/client.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
data/lib/keycloak_ruby/config.rb
CHANGED
@@ -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)
|
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
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
35
|
-
|
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
|
-
|
42
|
-
|
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
|
-
|
51
|
+
code = response.code
|
52
|
+
return if verify_response(code, request_params.success_codes)
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
KeycloakRuby.logger.error("#{
|
62
|
-
raise request_params.error_class, "#{
|
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: #{
|
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"]} - #{
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
59
|
+
config = OmniAuth.config
|
60
|
+
config.test_mode = true
|
60
61
|
token_data = generate_fake_tokens(user)
|
61
|
-
|
62
|
+
config.mock_auth[:keycloak] = token_data
|
62
63
|
|
63
|
-
|
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
|
-
|
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
|
-
|
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:
|
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
|
@@ -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
|
-
|
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
|
|
data/lib/keycloak_ruby/user.rb
CHANGED
@@ -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
|
-
|
61
|
-
|
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 }
|