gamora 0.8.0 → 0.10.0
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 +32 -0
- data/lib/gamora/authentication/base.rb +37 -20
- data/lib/gamora/authentication/headers.rb +1 -7
- data/lib/gamora/authentication/session.rb +2 -8
- data/lib/gamora/client.rb +27 -8
- data/lib/gamora/configuration.rb +5 -2
- data/lib/gamora/user.rb +1 -0
- data/lib/gamora/version.rb +1 -1
- data/lib/generators/gamora/templates/gamora.rb +5 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62fcee90396875db0256e297c90dda80546c3268cfecdd4f0e53b7f3c4e68ba8
|
4
|
+
data.tar.gz: '093571bfd8706530b474c1bf9d046db56cd5a0a53820546afc88f274f528a6a6'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0888b96495e6e55f0cc49512a9ec0c8d73d67e839a2dae0207bdb81728032e46436b8a3ce0cdf1daa1352d5ae849a4a723a28fe29d9c6c615b5efbf6dd33cc8c'
|
7
|
+
data.tar.gz: 3c00a26cce8ad257170853ffc0c6c9fd3a355372f401881b470cb9cdb86d0a94e43b401e5ac94024979faa3636969fc33ec7429f033287d006a6ff5bfa8360aa
|
data/README.md
CHANGED
@@ -115,6 +115,38 @@ Optionally, if you want to do something different when authentication
|
|
115
115
|
fails, you just need to override the `user_authentication_failed!`
|
116
116
|
method in you controller and customize it as you wish.
|
117
117
|
|
118
|
+
## Cross-Client Identity
|
119
|
+
|
120
|
+
By default, gamora will accept only access tokens that were generating
|
121
|
+
with the `client_id` in the configuration. If access tokens coming from
|
122
|
+
other clients have to be accepted, make sure to add their client ids to
|
123
|
+
the `whitelisted_clients` config option.
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
Gamora.setup do |config|
|
127
|
+
...
|
128
|
+
|
129
|
+
config.whitelisted_clients = ["OTHER_CLIENT_ID"]
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
## Caching
|
134
|
+
|
135
|
+
In order to avoid performing requests to the IDP on each request in the
|
136
|
+
application, it is possible to set a caching time for introspection and
|
137
|
+
userinfo endpoints. Make sure to not have a too long expiration time for
|
138
|
+
`introspect_cache_expires_in` but not too short to impact the application
|
139
|
+
performance, it is a balance.
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
Gamora.setup do |config|
|
143
|
+
...
|
144
|
+
|
145
|
+
config.userinfo_cache_expires_in = 10.minute
|
146
|
+
config.introspect_cache_expires_in = 5.seconds
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
118
150
|
## Development
|
119
151
|
|
120
152
|
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
@@ -7,6 +7,7 @@ module Gamora
|
|
7
7
|
sub: :id,
|
8
8
|
roles: :roles,
|
9
9
|
email: :email,
|
10
|
+
username: :username,
|
10
11
|
given_name: :first_name,
|
11
12
|
family_name: :last_name,
|
12
13
|
phone_number: :phone_number,
|
@@ -15,9 +16,15 @@ module Gamora
|
|
15
16
|
}.freeze
|
16
17
|
|
17
18
|
def authenticate_user!
|
19
|
+
return authentication_failed! unless access_token.present?
|
20
|
+
|
21
|
+
token_data = introspect_access_token(access_token)
|
22
|
+
return authentication_failed! unless valid_token_data?(token_data)
|
23
|
+
|
18
24
|
claims = resource_owner_claims(access_token)
|
19
|
-
|
20
|
-
|
25
|
+
return authentication_failed! unless claims.present?
|
26
|
+
|
27
|
+
assign_current_user_from_claims(claims)
|
21
28
|
end
|
22
29
|
|
23
30
|
def current_user
|
@@ -26,21 +33,29 @@ module Gamora
|
|
26
33
|
|
27
34
|
private
|
28
35
|
|
29
|
-
def
|
36
|
+
def access_token
|
30
37
|
raise NotImplementedError
|
31
38
|
end
|
32
39
|
|
33
|
-
def
|
40
|
+
def authentication_failed!
|
34
41
|
raise NotImplementedError
|
35
42
|
end
|
36
43
|
|
37
|
-
def
|
38
|
-
|
44
|
+
def valid_token_data?(token_data)
|
45
|
+
token_data[:active] && whitelisted_client?(token_data[:client_id])
|
46
|
+
end
|
47
|
+
|
48
|
+
def whitelisted_client?(client_id)
|
49
|
+
whitelisted_clients.include?(client_id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def whitelisted_clients
|
53
|
+
Configuration.whitelisted_clients | [Configuration.client_id]
|
39
54
|
end
|
40
55
|
|
41
56
|
def assign_current_user_from_claims(claims)
|
42
|
-
|
43
|
-
@current_user = User.new(
|
57
|
+
attributes = user_attributes_from_claims(claims)
|
58
|
+
@current_user = User.new(attributes)
|
44
59
|
end
|
45
60
|
|
46
61
|
def user_attributes_from_claims(claims)
|
@@ -48,27 +63,29 @@ module Gamora
|
|
48
63
|
end
|
49
64
|
|
50
65
|
def resource_owner_claims(access_token)
|
51
|
-
|
52
|
-
|
53
|
-
resource_owner_claims!(access_token)
|
54
|
-
end
|
66
|
+
cache_key = cache_key(:userinfo, access_token)
|
67
|
+
expires_in = Configuration.userinfo_cache_expires_in
|
55
68
|
|
56
|
-
|
57
|
-
Rails.cache.fetch(cache_key(access_token), cache_options) do
|
69
|
+
Rails.cache.fetch(cache_key, { expires_in: expires_in }) do
|
58
70
|
oauth_client.userinfo(access_token)
|
59
71
|
end
|
60
72
|
end
|
61
73
|
|
62
|
-
def
|
63
|
-
|
74
|
+
def introspect_access_token(access_token)
|
75
|
+
cache_key = cache_key(:introspect, access_token)
|
76
|
+
expires_in = Configuration.introspect_cache_expires_in
|
77
|
+
|
78
|
+
Rails.cache.fetch(cache_key, { expires_in: expires_in }) do
|
79
|
+
oauth_client.introspect(access_token)
|
80
|
+
end
|
64
81
|
end
|
65
82
|
|
66
|
-
def
|
67
|
-
{
|
83
|
+
def cache_key(context, access_token)
|
84
|
+
"gamora:#{context}:#{Digest::SHA256.hexdigest(access_token)}"
|
68
85
|
end
|
69
86
|
|
70
|
-
def
|
71
|
-
|
87
|
+
def oauth_client
|
88
|
+
Client.from_config
|
72
89
|
end
|
73
90
|
end
|
74
91
|
end
|
@@ -7,12 +7,6 @@ module Gamora
|
|
7
7
|
|
8
8
|
private
|
9
9
|
|
10
|
-
def validate_authentication!
|
11
|
-
return if current_user.present?
|
12
|
-
|
13
|
-
user_authentication_failed!
|
14
|
-
end
|
15
|
-
|
16
10
|
def access_token
|
17
11
|
pattern = /^Bearer /
|
18
12
|
header = request.headers["Authorization"]
|
@@ -21,7 +15,7 @@ module Gamora
|
|
21
15
|
header.gsub(pattern, "")
|
22
16
|
end
|
23
17
|
|
24
|
-
def
|
18
|
+
def authentication_failed!
|
25
19
|
render json: { error: "Access token invalid" }, status: :unauthorized
|
26
20
|
end
|
27
21
|
end
|
@@ -11,18 +11,12 @@ module Gamora
|
|
11
11
|
|
12
12
|
private
|
13
13
|
|
14
|
-
def validate_authentication!
|
15
|
-
return if current_user.present?
|
16
|
-
|
17
|
-
session["gamora.origin"] = request.original_url
|
18
|
-
user_authentication_failed!
|
19
|
-
end
|
20
|
-
|
21
14
|
def access_token
|
22
15
|
session[:access_token]
|
23
16
|
end
|
24
17
|
|
25
|
-
def
|
18
|
+
def authentication_failed!
|
19
|
+
session["gamora.origin"] = request.original_url
|
26
20
|
redirect_to gamora.authentication_path
|
27
21
|
end
|
28
22
|
end
|
data/lib/gamora/client.rb
CHANGED
@@ -20,13 +20,25 @@ module Gamora
|
|
20
20
|
token_method: Configuration.token_method,
|
21
21
|
redirect_uri: Configuration.redirect_uri,
|
22
22
|
userinfo_url: Configuration.userinfo_url,
|
23
|
-
authorize_url: Configuration.authorize_url
|
23
|
+
authorize_url: Configuration.authorize_url,
|
24
|
+
introspect_url: Configuration.introspect_url
|
24
25
|
}
|
25
26
|
end
|
26
27
|
end
|
27
28
|
|
28
29
|
def userinfo(access_token)
|
29
|
-
|
30
|
+
params = userinfo_params(access_token)
|
31
|
+
opts = request_options(params)
|
32
|
+
response = request(:post, options[:userinfo_url], opts)
|
33
|
+
JSON.parse(response.body).symbolize_keys
|
34
|
+
rescue OAuth2::Error
|
35
|
+
{}
|
36
|
+
end
|
37
|
+
|
38
|
+
def introspect(access_token)
|
39
|
+
params = introspect_params(access_token)
|
40
|
+
opts = request_options(params)
|
41
|
+
response = request(:post, options[:introspect_url], opts)
|
30
42
|
JSON.parse(response.body).symbolize_keys
|
31
43
|
rescue OAuth2::Error
|
32
44
|
{}
|
@@ -34,15 +46,22 @@ module Gamora
|
|
34
46
|
|
35
47
|
private
|
36
48
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
49
|
+
def request_options(params)
|
50
|
+
{
|
51
|
+
body: params.to_json,
|
52
|
+
headers: { "Content-Type": "application/json" }
|
53
|
+
}
|
40
54
|
end
|
41
55
|
|
42
|
-
def
|
56
|
+
def userinfo_params(access_token)
|
57
|
+
{ access_token: access_token }
|
58
|
+
end
|
59
|
+
|
60
|
+
def introspect_params(access_token)
|
43
61
|
{
|
44
|
-
|
45
|
-
|
62
|
+
token: access_token,
|
63
|
+
client_id: Configuration.client_id,
|
64
|
+
client_secret: Configuration.client_secret
|
46
65
|
}
|
47
66
|
end
|
48
67
|
end
|
data/lib/gamora/configuration.rb
CHANGED
@@ -6,8 +6,9 @@ module Gamora
|
|
6
6
|
mattr_accessor :client_secret, default: nil
|
7
7
|
mattr_accessor :site, default: nil
|
8
8
|
mattr_accessor :token_url, default: "/oauth2/token"
|
9
|
-
mattr_accessor :authorize_url, default: "/oauth2/authorize"
|
10
9
|
mattr_accessor :userinfo_url, default: "/oauth2/userinfo"
|
10
|
+
mattr_accessor :authorize_url, default: "/oauth2/authorize"
|
11
|
+
mattr_accessor :introspect_url, default: "/oauth2/introspect"
|
11
12
|
mattr_accessor :token_method, default: :post
|
12
13
|
mattr_accessor :redirect_uri, default: nil
|
13
14
|
mattr_accessor :default_scope, default: "openid profile email"
|
@@ -16,7 +17,9 @@ module Gamora
|
|
16
17
|
mattr_accessor :default_branding, default: "amco"
|
17
18
|
mattr_accessor :default_theme, default: "default"
|
18
19
|
mattr_accessor :ui_locales, default: -> { I18n.locale }
|
19
|
-
mattr_accessor :userinfo_cache_expires_in, default:
|
20
|
+
mattr_accessor :userinfo_cache_expires_in, default: 1.minute
|
21
|
+
mattr_accessor :introspect_cache_expires_in, default: 0.seconds
|
22
|
+
mattr_accessor :whitelisted_clients, default: []
|
20
23
|
|
21
24
|
def setup
|
22
25
|
yield(self) if block_given?
|
data/lib/gamora/user.rb
CHANGED
data/lib/gamora/version.rb
CHANGED
@@ -8,8 +8,9 @@ Gamora.setup do |config|
|
|
8
8
|
|
9
9
|
# ===> Optional OAuth2 configuration options and its defaults.
|
10
10
|
# config.token_url = "/oauth2/token"
|
11
|
-
# config.authorize_url = "/oauth2/authorize"
|
12
11
|
# config.userinfo_url = "/oauth2/userinfo"
|
12
|
+
# config.authorize_url = "/oauth2/authorize"
|
13
|
+
# config.introspect_url = "/oauth2/introspect"
|
13
14
|
# config.token_method = :post
|
14
15
|
# config.redirect_uri = nil
|
15
16
|
# config.default_scope = "openid profile email"
|
@@ -18,5 +19,7 @@ Gamora.setup do |config|
|
|
18
19
|
# config.default_branding = "amco"
|
19
20
|
# config.default_theme = "default"
|
20
21
|
# config.ui_locales = -> { I18n.locale }
|
21
|
-
# config.userinfo_cache_expires_in =
|
22
|
+
# config.userinfo_cache_expires_in = 1.minute
|
23
|
+
# config.introspect_cache_expires_in = 0.seconds
|
24
|
+
# config.whitelisted_clients = []
|
22
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gamora
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alejandro Gutiérrez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: oauth2
|