keycloak-api-rails 1.1.1 → 2.0.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/CHANGELOG.md +60 -0
- data/MIT-LICENSE +1 -1
- data/README.md +51 -21
- data/keycloak-api-rails.gemspec +16 -6
- data/lib/keycloak-api-rails/authentication.rb +3 -11
- data/lib/keycloak-api-rails/configuration.rb +84 -0
- data/lib/keycloak-api-rails/helper.rb +31 -4
- data/lib/keycloak-api-rails/http_client.rb +54 -19
- data/lib/keycloak-api-rails/middleware.rb +17 -20
- data/lib/keycloak-api-rails/public_key_cached_resolver.rb +26 -7
- data/lib/keycloak-api-rails/railtie.rb +16 -1
- data/lib/keycloak-api-rails/service.rb +77 -28
- data/lib/keycloak-api-rails/testing.rb +5 -3
- data/lib/keycloak-api-rails/token_error.rb +41 -23
- data/lib/keycloak-api-rails/version.rb +1 -1
- data/lib/keycloak-api-rails.rb +21 -6
- metadata +39 -23
- data/.github/workflows/ci.yml +0 -35
- data/.github/workflows/release.yml +0 -52
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/Dockerfile +0 -13
- data/Gemfile +0 -3
- data/Rakefile +0 -6
- data/spec/keycloak-api-rails/activesupport_free_spec.rb +0 -18
- data/spec/keycloak-api-rails/authentication_spec.rb +0 -70
- data/spec/keycloak-api-rails/configuration_spec.rb +0 -101
- data/spec/keycloak-api-rails/helper_spec.rb +0 -94
- data/spec/keycloak-api-rails/public_key_cached_resolver_spec.rb +0 -80
- data/spec/keycloak-api-rails/service_spec.rb +0 -281
- data/spec/keycloak-api-rails/testing_spec.rb +0 -102
- data/spec/spec_helper.rb +0 -14
- data/spec/support/activesupport_free_probe.rb +0 -131
- data/spec/support/public_key_cached_resolver_stub.rb +0 -11
- data/spec/support/public_key_resolver_stub.rb +0 -7
- data/spec/support/rails_helper.rb +0 -4
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
|
|
3
|
-
describe KeycloakApiRails::Authentication do
|
|
4
|
-
class ExampleController < ActionController::Base
|
|
5
|
-
include KeycloakApiRails::Authentication
|
|
6
|
-
# Mark protected methods public so they may be called in tests
|
|
7
|
-
public(*KeycloakApiRails::Authentication.protected_instance_methods)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
let(:controller) { ExampleController.new }
|
|
11
|
-
let(:token) { 'keycloak_valid_token'}
|
|
12
|
-
let(:headers) do
|
|
13
|
-
{
|
|
14
|
-
'REQUEST_METHOD' => :get,
|
|
15
|
-
'REQUEST_URI' => 'http://www.an-url.io',
|
|
16
|
-
'HTTP_AUTHORIZATION' => "Bearer #{token}"
|
|
17
|
-
}
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
describe "when included" do
|
|
21
|
-
it "declares keycloak_authenticate as a helper method when the base class supports helpers" do
|
|
22
|
-
declared_helper_methods = []
|
|
23
|
-
base = Class.new do
|
|
24
|
-
define_singleton_method(:helper_method) { |*names| declared_helper_methods.concat(names) }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
base.include(KeycloakApiRails::Authentication)
|
|
28
|
-
|
|
29
|
-
expect(declared_helper_methods).to eq [:keycloak_authenticate]
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it "declares the helper method on a real controller" do
|
|
33
|
-
expect(ExampleController._helper_methods).to include :keycloak_authenticate
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it "does not fail when the base class does not support helpers" do
|
|
37
|
-
expect {
|
|
38
|
-
Class.new.include(KeycloakApiRails::Authentication)
|
|
39
|
-
}.to_not raise_error
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "exposes its methods as protected instance methods" do
|
|
43
|
-
expect(KeycloakApiRails::Authentication.protected_instance_methods).to match_array %i[
|
|
44
|
-
keycloak_authenticate
|
|
45
|
-
authentication_failed
|
|
46
|
-
authentication_succeeded
|
|
47
|
-
]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "does not add any public method to the including class" do
|
|
51
|
-
base = Class.new.include(KeycloakApiRails::Authentication)
|
|
52
|
-
|
|
53
|
-
expect(base.public_instance_methods).to_not include :keycloak_authenticate
|
|
54
|
-
expect(base.protected_instance_methods).to include :keycloak_authenticate
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
describe "#keycloak_authenticate" do
|
|
59
|
-
before do
|
|
60
|
-
# Mock request object because we aren't using real request spec
|
|
61
|
-
allow(controller).to receive(:request).and_return(double("request", env: headers ))
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it "it authenticates with request header" do
|
|
65
|
-
expect_any_instance_of(KeycloakApiRails::Service).to receive(:decode_and_verify).with(token).and_return("A User")
|
|
66
|
-
expect(controller).to receive(:authentication_succeeded)
|
|
67
|
-
controller.keycloak_authenticate
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
RSpec.describe KeycloakApiRails::Configuration do
|
|
2
|
-
|
|
3
|
-
# Every attribute that used to be declared through ActiveSupport's `config_accessor`.
|
|
4
|
-
let(:attribute_names) do
|
|
5
|
-
%i[
|
|
6
|
-
server_url
|
|
7
|
-
realm_id
|
|
8
|
-
skip_paths
|
|
9
|
-
opt_in
|
|
10
|
-
token_expiration_tolerance_in_seconds
|
|
11
|
-
public_key_cache_ttl
|
|
12
|
-
custom_attributes
|
|
13
|
-
logger
|
|
14
|
-
ca_certificate_file
|
|
15
|
-
]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
describe "attributes" do
|
|
19
|
-
let(:configuration) { KeycloakApiRails::Configuration.new }
|
|
20
|
-
|
|
21
|
-
it "exposes a reader and a writer for each attribute" do
|
|
22
|
-
attribute_names.each do |name|
|
|
23
|
-
expect(configuration).to respond_to name
|
|
24
|
-
expect(configuration).to respond_to "#{name}="
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "returns nil for each attribute that has not been assigned" do
|
|
29
|
-
attribute_names.each do |name|
|
|
30
|
-
expect(configuration.public_send(name)).to be_nil
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
it "returns the value that has been assigned" do
|
|
35
|
-
attribute_names.each do |name|
|
|
36
|
-
configuration.public_send("#{name}=", "a value for #{name}")
|
|
37
|
-
expect(configuration.public_send(name)).to eq "a value for #{name}"
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "does not respond to an unknown attribute" do
|
|
42
|
-
expect(configuration).to_not respond_to :not_a_configuration_attribute
|
|
43
|
-
expect {
|
|
44
|
-
configuration.not_a_configuration_attribute
|
|
45
|
-
}.to raise_error NoMethodError
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
describe "#configure" do
|
|
50
|
-
after(:each) do
|
|
51
|
-
KeycloakApiRails.load_configuration
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it "yields the configuration" do
|
|
55
|
-
expect { |block| KeycloakApiRails.configure(&block) }.to yield_with_args KeycloakApiRails::Configuration
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
it "keeps the values assigned within the block" do
|
|
59
|
-
KeycloakApiRails.configure do |config|
|
|
60
|
-
config.server_url = "http://keycloak:8080"
|
|
61
|
-
config.realm_id = "a-realm"
|
|
62
|
-
config.ca_certificate_file = "/etc/ssl/a-certificate.pem"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
expect(KeycloakApiRails.config.server_url).to eq "http://keycloak:8080"
|
|
66
|
-
expect(KeycloakApiRails.config.realm_id).to eq "a-realm"
|
|
67
|
-
expect(KeycloakApiRails.config.ca_certificate_file).to eq "/etc/ssl/a-certificate.pem"
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "always returns the same configuration instance" do
|
|
71
|
-
expect(KeycloakApiRails.config).to be KeycloakApiRails.config
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "#load_configuration" do
|
|
76
|
-
before(:each) do
|
|
77
|
-
KeycloakApiRails.load_configuration
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
after(:each) do
|
|
81
|
-
KeycloakApiRails.load_configuration
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it "applies the documented default values" do
|
|
85
|
-
config = KeycloakApiRails.config
|
|
86
|
-
|
|
87
|
-
expect(config.server_url).to be_nil
|
|
88
|
-
expect(config.realm_id).to be_nil
|
|
89
|
-
expect(config.logger).to be_a ::Logger
|
|
90
|
-
expect(config.skip_paths).to eq({})
|
|
91
|
-
expect(config.opt_in).to be false
|
|
92
|
-
expect(config.token_expiration_tolerance_in_seconds).to eq 10
|
|
93
|
-
expect(config.public_key_cache_ttl).to eq 86400
|
|
94
|
-
expect(config.custom_attributes).to eq []
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
it "exposes the logger through KeycloakApiRails.logger" do
|
|
98
|
-
expect(KeycloakApiRails.logger).to be KeycloakApiRails.config.logger
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
RSpec.describe KeycloakApiRails::Helper do
|
|
2
|
-
describe "#read_token_from_query_string" do
|
|
3
|
-
|
|
4
|
-
subject { KeycloakApiRails::Helper.read_token_from_query_string(uri) }
|
|
5
|
-
|
|
6
|
-
context "when the uri is nil" do
|
|
7
|
-
let(:uri) { nil }
|
|
8
|
-
it "returns an empty token" do
|
|
9
|
-
expect(subject).to eq ""
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
context "when the uri is empty" do
|
|
14
|
-
let(:uri) { "" }
|
|
15
|
-
it "returns an empty token" do
|
|
16
|
-
expect(subject).to eq ""
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
context "when the uri only contains whitespaces" do
|
|
21
|
-
let(:uri) { " " }
|
|
22
|
-
it "returns an empty token" do
|
|
23
|
-
expect(subject).to eq ""
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
context "when the uri has no query string" do
|
|
28
|
-
let(:uri) { "http://www.an-url.io/health" }
|
|
29
|
-
it "returns no token" do
|
|
30
|
-
expect(subject).to be_nil
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
context "when the uri has a query string but no token" do
|
|
35
|
-
let(:uri) { "http://www.an-url.io/health?firstName=ouioui" }
|
|
36
|
-
it "returns no token" do
|
|
37
|
-
expect(subject).to be_nil
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
context "when the uri has a token in the query string" do
|
|
42
|
-
let(:uri) { "http://www.an-url.io/health?authorizationToken=aToken" }
|
|
43
|
-
it "returns the token" do
|
|
44
|
-
expect(subject).to eq "aToken"
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
context "when the uri has a token among other query string parameters" do
|
|
49
|
-
let(:uri) { "http://www.an-url.io/health?firstName=ouioui&authorizationToken=aToken&lastName=nonnon" }
|
|
50
|
-
it "returns the token" do
|
|
51
|
-
expect(subject).to eq "aToken"
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
context "when the uri has an empty token in the query string" do
|
|
56
|
-
let(:uri) { "http://www.an-url.io/health?authorizationToken=" }
|
|
57
|
-
it "returns an empty token" do
|
|
58
|
-
expect(subject).to eq ""
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
describe "#create_url_with_token" do
|
|
64
|
-
|
|
65
|
-
let(:uri) { "http://www.an-url.io" }
|
|
66
|
-
let(:token) { "aToken" }
|
|
67
|
-
|
|
68
|
-
before(:each) do
|
|
69
|
-
@url_with_token = KeycloakApiRails::Helper.create_url_with_token(uri, token)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
context "when the uri has no query string yet" do
|
|
73
|
-
it "returns an url with the provided token" do
|
|
74
|
-
expect(@url_with_token).to eq "#{uri}?authorizationToken=#{token}"
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
context "when the uri already has no query strings" do
|
|
79
|
-
context "but no token yet" do
|
|
80
|
-
let(:uri) { "http://www.an-url.io?firstName=ouioui&lastName=nonnon" }
|
|
81
|
-
it "returns an url with all the query string and the token" do
|
|
82
|
-
expect(@url_with_token).to eq "#{uri}&authorizationToken=#{token}"
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
context "including a token" do
|
|
87
|
-
let(:uri) { "http://www.an-url.io?authorizationToken=ouioui&lastName=nonnon" }
|
|
88
|
-
it "returns an url with all the query string and the new token" do
|
|
89
|
-
expect(@url_with_token).to eq "http://www.an-url.io?lastName=nonnon&authorizationToken=#{token}"
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
RSpec.describe KeycloakApiRails::Service do
|
|
2
|
-
|
|
3
|
-
let(:public_key_cache_ttl) { 86400 }
|
|
4
|
-
let(:server_url) { "whatever:8080" }
|
|
5
|
-
let(:realm_id) { "pouet" }
|
|
6
|
-
let!(:resolver) { KeycloakApiRails::PublicKeyCachedResolver.new(server_url, realm_id, public_key_cache_ttl) }
|
|
7
|
-
|
|
8
|
-
before(:each) do
|
|
9
|
-
resolver.instance_variable_set(:@resolver, KeycloakApiRails::PublicKeyResolverStub.new)
|
|
10
|
-
now = Time.local(2018, 1, 9, 12, 0, 0)
|
|
11
|
-
Timecop.freeze(now)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
after(:each) do
|
|
15
|
-
Timecop.return
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
describe "#find_public_key" do
|
|
19
|
-
context "when there is no public key in cache yet" do
|
|
20
|
-
before(:each) do
|
|
21
|
-
@public_key = resolver.find_public_keys
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it "returns a valid public key" do
|
|
25
|
-
expect(@public_key).to_not be_nil
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "sets the current time to the resolver" do
|
|
29
|
-
expect(resolver.cached_public_key_retrieved_at).to eq Time.now
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
context "when there is already a public key in cache" do
|
|
34
|
-
before(:each) do
|
|
35
|
-
@first_public_key = resolver.find_public_keys
|
|
36
|
-
@first_cached_public_key_retrieved_at = resolver.cached_public_key_retrieved_at
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
context "and no need to refresh it" do
|
|
40
|
-
before(:each) do
|
|
41
|
-
Timecop.freeze(Time.now + public_key_cache_ttl.seconds - 10.seconds)
|
|
42
|
-
@second_public_key = resolver.find_public_keys
|
|
43
|
-
@second_cached_public_key_retrieved_at = resolver.cached_public_key_retrieved_at
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it "returns a valid public key" do
|
|
47
|
-
expect(@second_public_key).to_not be_nil
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it "does not refresh the public key" do
|
|
51
|
-
expect(@second_public_key).to eq @first_public_key
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it "does not refresh the public key retrieval time" do
|
|
55
|
-
expect(@first_cached_public_key_retrieved_at).to eq @second_cached_public_key_retrieved_at
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
context "and its TTL has expired" do
|
|
60
|
-
before(:each) do
|
|
61
|
-
Timecop.freeze(Time.now + public_key_cache_ttl.seconds + 10.seconds)
|
|
62
|
-
@second_public_key = resolver.find_public_keys
|
|
63
|
-
@second_cached_public_key_retrieved_at = resolver.cached_public_key_retrieved_at
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it "returns a valid public key" do
|
|
67
|
-
expect(@second_public_key).to_not be_nil
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "refreshes the public key" do
|
|
71
|
-
expect(@second_public_key).to_not eq @first_public_key
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
it "refreshes the public key retrieval time" do
|
|
75
|
-
expect(@first_cached_public_key_retrieved_at).to_not eq @second_cached_public_key_retrieved_at
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
RSpec.describe KeycloakApiRails::Service do
|
|
2
|
-
|
|
3
|
-
let!(:private_key) { OpenSSL::PKey::RSA.generate(2048) }
|
|
4
|
-
let!(:public_key) { private_key.public_key }
|
|
5
|
-
let!(:key_resolver) { KeycloakApiRails::PublicKeyCachedResolverStub.new(public_key) }
|
|
6
|
-
let!(:service) { KeycloakApiRails::Service.new(key_resolver) }
|
|
7
|
-
|
|
8
|
-
before(:each) do
|
|
9
|
-
now = Time.local(2018, 1, 9, 12, 0, 0)
|
|
10
|
-
Timecop.freeze(now)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
after(:each) do
|
|
14
|
-
Timecop.return
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
describe "#decode_and_verify" do
|
|
18
|
-
def create_token(private_key, expiration_date, algorithm)
|
|
19
|
-
claim = {
|
|
20
|
-
iss: "KeycloakApiRails",
|
|
21
|
-
exp: expiration_date,
|
|
22
|
-
nbf: Time.local(2018, 1, 1, 0, 0, 0)
|
|
23
|
-
}
|
|
24
|
-
jws = JSON::JWT.new(claim).sign(private_key, algorithm)
|
|
25
|
-
jws.to_s
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
context "when token is nil" do
|
|
29
|
-
let(:token) { nil }
|
|
30
|
-
it "should raise an error :no_token" do
|
|
31
|
-
expect {
|
|
32
|
-
service.decode_and_verify(token)
|
|
33
|
-
}.to raise_error(TokenError, "No JWT token provided")
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
context "when token is an empty string" do
|
|
38
|
-
let(:token) { "" }
|
|
39
|
-
it "should raise an error :no_token" do
|
|
40
|
-
expect {
|
|
41
|
-
service.decode_and_verify(token)
|
|
42
|
-
}.to raise_error(TokenError, "No JWT token provided")
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
context "when token is in an invalid format" do
|
|
47
|
-
let(:token) { "coucou" }
|
|
48
|
-
it "should raise an error :invalid_format" do
|
|
49
|
-
expect {
|
|
50
|
-
service.decode_and_verify(token)
|
|
51
|
-
}.to raise_error(TokenError, "Wrong JWT Format")
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
context "when token is in a valid format" do
|
|
56
|
-
let(:algorithm) { :RS256 }
|
|
57
|
-
let(:expiration_date) { 1.week.from_now }
|
|
58
|
-
|
|
59
|
-
context "and token is generated by another private key" do
|
|
60
|
-
let(:another_private_key) { OpenSSL::PKey::RSA.generate(1024) }
|
|
61
|
-
let(:token) { create_token(another_private_key, expiration_date, algorithm) }
|
|
62
|
-
|
|
63
|
-
it "should raise an error :verification_failed" do
|
|
64
|
-
expect {
|
|
65
|
-
service.decode_and_verify(token)
|
|
66
|
-
}.to raise_error(TokenError, "Failed to verify JWT token")
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
context "and token is generated by the right private key" do
|
|
71
|
-
let(:token) { create_token(private_key, expiration_date, algorithm) }
|
|
72
|
-
|
|
73
|
-
context "and token is expired" do
|
|
74
|
-
let(:expiration_date) { Time.now - 2.days }
|
|
75
|
-
|
|
76
|
-
it "should raise an error :expiration_date" do
|
|
77
|
-
expect {
|
|
78
|
-
service.decode_and_verify(token)
|
|
79
|
-
}.to raise_error(TokenError, "JWT token is expired")
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# The service reads `token_expiration_tolerance_in_seconds` when it is instantiated:
|
|
84
|
-
# a token expiring within that tolerance is already considered as expired.
|
|
85
|
-
context "and token expires within the expiration tolerance" do
|
|
86
|
-
let(:expiration_date) { Time.now + 5 }
|
|
87
|
-
|
|
88
|
-
it "should raise an error :expiration_date" do
|
|
89
|
-
expect(KeycloakApiRails.config.token_expiration_tolerance_in_seconds).to eq 10
|
|
90
|
-
expect {
|
|
91
|
-
service.decode_and_verify(token)
|
|
92
|
-
}.to raise_error(TokenError, "JWT token is expired")
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
context "and token expires just after the expiration tolerance" do
|
|
97
|
-
let(:expiration_date) { Time.now + 30 }
|
|
98
|
-
|
|
99
|
-
it "should return a not-nil decoded token" do
|
|
100
|
-
expect(KeycloakApiRails.config.token_expiration_tolerance_in_seconds).to eq 10
|
|
101
|
-
expect(service.decode_and_verify(token)).to_not be_nil
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
context "and token is not expired" do
|
|
106
|
-
let(:expiration_date) { Time.now + 2.days }
|
|
107
|
-
|
|
108
|
-
context "and token is encrypted using RS256" do
|
|
109
|
-
let(:algorithm) { :RS256 }
|
|
110
|
-
|
|
111
|
-
it "should return a not-nil decoded token" do
|
|
112
|
-
expect(service.decode_and_verify(token)).to_not be_nil
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
context "and token is encrypted using RS512" do
|
|
117
|
-
let(:algorithm) { :RS512 }
|
|
118
|
-
|
|
119
|
-
it "should return a not-nil decoded token" do
|
|
120
|
-
expect(service.decode_and_verify(token)).to_not be_nil
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
describe "#need_middleware_authentication?" do
|
|
129
|
-
|
|
130
|
-
let(:method) { nil }
|
|
131
|
-
let(:path) { nil }
|
|
132
|
-
let(:headers) { {} }
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
before(:each) do
|
|
136
|
-
KeycloakApiRails.config.skip_paths = {
|
|
137
|
-
post: [/^\/skip/],
|
|
138
|
-
get: [/^\/skip/]
|
|
139
|
-
}
|
|
140
|
-
@result = service.need_middleware_authentication?(method, path, headers)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
context "when method is nil" do
|
|
144
|
-
let(:method) { nil }
|
|
145
|
-
let(:path) { "/do-not-skip" }
|
|
146
|
-
it "should return true" do
|
|
147
|
-
expect(@result).to be true
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
context "when path is nil" do
|
|
152
|
-
let(:method) { :get }
|
|
153
|
-
let(:path) { nil }
|
|
154
|
-
it "should return true" do
|
|
155
|
-
expect(@result).to be true
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
context "when method does not match the configuration" do
|
|
160
|
-
let(:method) { :put }
|
|
161
|
-
let(:path) { "/skip" }
|
|
162
|
-
it "should return true" do
|
|
163
|
-
expect(@result).to be true
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
context "when path does not match the configuration" do
|
|
168
|
-
let(:method) { :get }
|
|
169
|
-
let(:path) { "/do-not-skip" }
|
|
170
|
-
it "should return true" do
|
|
171
|
-
expect(@result).to be true
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
context "when method [get] and path do match the configuration" do
|
|
176
|
-
let(:method) { :get }
|
|
177
|
-
let(:path) { "/skip" }
|
|
178
|
-
it "should return false" do
|
|
179
|
-
expect(@result).to be false
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
context "when method [post] and path do match the configuration" do
|
|
185
|
-
let(:method) { :get }
|
|
186
|
-
let(:path) { "/skip" }
|
|
187
|
-
it "should return false" do
|
|
188
|
-
expect(@result).to be false
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
context "when the request is preflight" do
|
|
193
|
-
let(:method) { :options }
|
|
194
|
-
let(:headers) { { "HTTP_ACCESS_CONTROL_REQUEST_METHOD" => ["Authorization"] } }
|
|
195
|
-
let(:path) { "/do-not-skip" }
|
|
196
|
-
it "should return false" do
|
|
197
|
-
expect(@result).to be false
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
context "when configured as opt_in" do
|
|
202
|
-
before do
|
|
203
|
-
KeycloakApiRails.config.opt_in = true
|
|
204
|
-
service2 = KeycloakApiRails::Service.new(key_resolver)
|
|
205
|
-
@result = service2.need_middleware_authentication?(method, path, headers)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
# The configuration is global: leaving 'opt_in' enabled would disable the authentication
|
|
209
|
-
# of every example running afterwards.
|
|
210
|
-
after do
|
|
211
|
-
KeycloakApiRails.config.opt_in = false
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it "should return false" do
|
|
215
|
-
expect(@result).to be false
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
describe "#read_token" do
|
|
221
|
-
let(:query_string) { "" }
|
|
222
|
-
let(:url) { "http://api.service.com/api/health?aParameter=true#{query_string}" }
|
|
223
|
-
let(:headers) { {} }
|
|
224
|
-
let(:header_token) { "header_token" }
|
|
225
|
-
let(:query_string_token) { "query_string_token" }
|
|
226
|
-
|
|
227
|
-
before(:each) do
|
|
228
|
-
@token = service.read_token(url, headers)
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
context "when the token is provided in the Authorization headers" do
|
|
232
|
-
let(:headers) do
|
|
233
|
-
{
|
|
234
|
-
"HTTP_AUTHORIZATION" => "Bearer #{header_token}"
|
|
235
|
-
}
|
|
236
|
-
end
|
|
237
|
-
context "and not in the query string" do
|
|
238
|
-
let(:query_string) { "" }
|
|
239
|
-
it "returns the header token" do
|
|
240
|
-
expect(@token).to eq header_token
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
context "and also in the query string" do
|
|
245
|
-
let(:query_string) { "&authorizationToken=#{query_string_token}" }
|
|
246
|
-
it "returns the query string token" do
|
|
247
|
-
expect(@token).to eq query_string_token
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
context "when the token is not provided in the Authorization headers" do
|
|
253
|
-
let(:headers) do
|
|
254
|
-
{
|
|
255
|
-
"ANOTHER_HEADER" => header_token
|
|
256
|
-
}
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
context "and not in the query string" do
|
|
260
|
-
let(:query_string) { "" }
|
|
261
|
-
it "returns an empty token" do
|
|
262
|
-
expect(@token).to eq ""
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
context "and query string is nil" do
|
|
267
|
-
let(:query_string) { nil }
|
|
268
|
-
it "returns an empty token" do
|
|
269
|
-
expect(@token).to eq ""
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
context "but in the query string" do
|
|
274
|
-
let(:query_string) { "&authorizationToken=#{query_string_token}" }
|
|
275
|
-
it "returns the query string token" do
|
|
276
|
-
expect(@token).to eq query_string_token
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
end
|