doorkeeper 5.1.0.rc1 → 5.1.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +11 -2
- data/Appraisals +29 -3
- data/Gemfile +13 -5
- data/NEWS.md +52 -15
- data/README.md +68 -487
- data/app/controllers/doorkeeper/token_info_controller.rb +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +1 -1
- data/doorkeeper.gemspec +3 -2
- data/gemfiles/rails_4_2.gemfile +8 -5
- data/gemfiles/rails_5_0.gemfile +9 -6
- data/gemfiles/rails_5_1.gemfile +9 -6
- data/gemfiles/rails_5_2.gemfile +9 -6
- data/gemfiles/rails_6_0.gemfile +16 -0
- data/gemfiles/rails_master.gemfile +8 -10
- data/lib/doorkeeper.rb +7 -1
- data/lib/doorkeeper/config.rb +110 -24
- data/lib/doorkeeper/models/access_grant_mixin.rb +15 -7
- data/lib/doorkeeper/models/access_token_mixin.rb +29 -16
- data/lib/doorkeeper/models/application_mixin.rb +18 -28
- data/lib/doorkeeper/models/concerns/expirable.rb +3 -2
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +4 -0
- data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +3 -1
- data/lib/doorkeeper/oauth/error_response.rb +5 -1
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +12 -1
- data/lib/doorkeeper/oauth/invalid_token_response.rb +4 -0
- data/lib/doorkeeper/oauth/token_introspection.rb +72 -6
- data/lib/doorkeeper/orm/active_record/access_grant.rb +9 -8
- data/lib/doorkeeper/orm/active_record/application.rb +10 -6
- data/lib/doorkeeper/secret_storing/base.rb +63 -0
- data/lib/doorkeeper/secret_storing/bcrypt.rb +59 -0
- data/lib/doorkeeper/secret_storing/plain.rb +33 -0
- data/lib/doorkeeper/secret_storing/sha256_hash.rb +25 -0
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/generators/doorkeeper/templates/initializer.rb +62 -20
- data/spec/controllers/authorizations_controller_spec.rb +3 -3
- data/spec/controllers/token_info_controller_spec.rb +1 -1
- data/spec/controllers/tokens_controller_spec.rb +78 -30
- data/spec/dummy/config/application.rb +12 -1
- data/spec/lib/config_spec.rb +119 -35
- data/spec/lib/models/expirable_spec.rb +12 -0
- data/spec/lib/models/reusable_spec.rb +40 -0
- data/spec/lib/models/scopes_spec.rb +13 -1
- data/spec/lib/models/secret_storable_spec.rb +113 -0
- data/spec/lib/oauth/authorization_code_request_spec.rb +18 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +51 -7
- data/spec/lib/oauth/error_response_spec.rb +7 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +11 -1
- data/spec/lib/oauth/token_request_spec.rb +16 -1
- data/spec/lib/secret_storing/base_spec.rb +60 -0
- data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
- data/spec/lib/secret_storing/plain_spec.rb +44 -0
- data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
- data/spec/models/doorkeeper/application_spec.rb +23 -4
- data/spec/requests/flows/authorization_code_spec.rb +3 -3
- data/spec/requests/flows/client_credentials_spec.rb +2 -2
- data/spec/requests/flows/implicit_grant_spec.rb +1 -1
- data/spec/requests/flows/password_spec.rb +3 -3
- data/spec/routing/custom_controller_routes_spec.rb +4 -0
- data/spec/support/shared/hashing_shared_context.rb +12 -5
- metadata +51 -21
- data/lib/doorkeeper/models/concerns/hashable.rb +0 -137
- data/spec/lib/models/hashable_spec.rb +0 -183
@@ -1,6 +1,17 @@
|
|
1
1
|
require File.expand_path('boot', __dir__)
|
2
2
|
|
3
|
-
require
|
3
|
+
require "rails"
|
4
|
+
|
5
|
+
%w[
|
6
|
+
action_controller/railtie
|
7
|
+
action_view/railtie
|
8
|
+
sprockets/railtie
|
9
|
+
].each do |railtie|
|
10
|
+
begin
|
11
|
+
require railtie
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
end
|
4
15
|
|
5
16
|
Bundler.require(*Rails.groups)
|
6
17
|
|
data/spec/lib/config_spec.rb
CHANGED
@@ -207,6 +207,31 @@ describe Doorkeeper, 'configuration' do
|
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
210
|
+
describe 'token_reuse_limit' do
|
211
|
+
it 'is 100 by default' do
|
212
|
+
expect(subject.token_reuse_limit).to eq(100)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'can change the value' do
|
216
|
+
Doorkeeper.configure do
|
217
|
+
token_reuse_limit 90
|
218
|
+
end
|
219
|
+
|
220
|
+
expect(subject.token_reuse_limit).to eq(90)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'sets the value to 100 if invalid value is being set' do
|
224
|
+
expect(Rails.logger).to receive(:warn).with(/will be set to default 100/)
|
225
|
+
|
226
|
+
Doorkeeper.configure do
|
227
|
+
reuse_access_token
|
228
|
+
token_reuse_limit 110
|
229
|
+
end
|
230
|
+
|
231
|
+
expect(subject.token_reuse_limit).to eq(100)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
210
235
|
describe 'enforce_configured_scopes' do
|
211
236
|
it 'is false by default' do
|
212
237
|
expect(subject.enforce_configured_scopes?).to eq(false)
|
@@ -457,6 +482,22 @@ describe Doorkeeper, 'configuration' do
|
|
457
482
|
end
|
458
483
|
end
|
459
484
|
|
485
|
+
describe 'default_generator_method' do
|
486
|
+
it "is :urlsafe_base64 by default" do
|
487
|
+
expect(Doorkeeper.configuration.default_generator_method)
|
488
|
+
.to eq(:urlsafe_base64)
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'can change the value' do
|
492
|
+
Doorkeeper.configure do
|
493
|
+
orm DOORKEEPER_ORM
|
494
|
+
default_generator_method :hex
|
495
|
+
end
|
496
|
+
|
497
|
+
expect(subject.default_generator_method).to eq(:hex)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
460
501
|
describe 'base_controller' do
|
461
502
|
context 'default' do
|
462
503
|
it { expect(Doorkeeper.configuration.base_controller).to eq('ActionController::Base') }
|
@@ -542,71 +583,114 @@ describe Doorkeeper, 'configuration' do
|
|
542
583
|
end
|
543
584
|
end
|
544
585
|
|
545
|
-
describe '
|
546
|
-
it 'is
|
547
|
-
expect(subject.
|
548
|
-
expect(
|
586
|
+
describe 'token_secret_strategy' do
|
587
|
+
it 'is plain by default' do
|
588
|
+
expect(subject.token_secret_strategy).to eq(Doorkeeper::SecretStoring::Plain)
|
589
|
+
expect(subject.token_secret_fallback_strategy).to eq(nil)
|
549
590
|
end
|
550
591
|
|
551
592
|
context 'when provided' do
|
552
593
|
before do
|
553
594
|
Doorkeeper.configure do
|
554
|
-
|
555
|
-
hash_application_secrets
|
595
|
+
hash_token_secrets
|
556
596
|
end
|
557
597
|
end
|
558
598
|
|
559
599
|
it 'will enable hashing for applications' do
|
560
|
-
expect(subject.
|
561
|
-
expect(subject.
|
600
|
+
expect(subject.token_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)
|
601
|
+
expect(subject.token_secret_fallback_strategy).to eq(nil)
|
602
|
+
end
|
603
|
+
end
|
562
604
|
|
563
|
-
|
605
|
+
context 'when manually provided with invalid constant' do
|
606
|
+
it 'raises an exception' do
|
607
|
+
expect {
|
608
|
+
Doorkeeper.configure do
|
609
|
+
hash_token_secrets using: 'does not exist'
|
610
|
+
end
|
611
|
+
}.to raise_error(NameError)
|
564
612
|
end
|
565
613
|
end
|
566
|
-
end
|
567
614
|
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
615
|
+
context 'when manually provided with invalid option' do
|
616
|
+
it 'raises an exception' do
|
617
|
+
expect do
|
618
|
+
Doorkeeper.configure do
|
619
|
+
hash_token_secrets using: 'Doorkeeper::SecretStoring::BCrypt'
|
620
|
+
end
|
621
|
+
end.to raise_error(ArgumentError,
|
622
|
+
/can only be used for storing application secrets/)
|
623
|
+
end
|
573
624
|
end
|
574
625
|
|
575
|
-
context 'when provided' do
|
576
|
-
|
626
|
+
context 'when provided with fallback' do
|
627
|
+
before do
|
628
|
+
Doorkeeper.configure do
|
629
|
+
hash_token_secrets fallback: :plain
|
630
|
+
end
|
631
|
+
end
|
577
632
|
|
578
|
-
it 'will enable hashing for
|
579
|
-
expect(subject.
|
580
|
-
expect(
|
581
|
-
|
633
|
+
it 'will enable hashing for applications' do
|
634
|
+
expect(subject.token_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)
|
635
|
+
expect(subject.token_secret_fallback_strategy).to eq(Doorkeeper::SecretStoring::Plain)
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
|
640
|
+
describe 'hash_token_secrets together with reuse_access_token' do
|
641
|
+
it 'will disable reuse_access_token' do
|
642
|
+
expect(Rails.logger).to receive(:warn).with(/reuse_access_token will be disabled/)
|
643
|
+
|
644
|
+
Doorkeeper.configure do
|
645
|
+
reuse_access_token
|
646
|
+
hash_token_secrets
|
647
|
+
end
|
648
|
+
|
649
|
+
expect(subject.reuse_access_token).to eq(false)
|
582
650
|
end
|
583
651
|
end
|
584
652
|
end
|
585
653
|
|
586
|
-
describe '
|
587
|
-
it 'is
|
588
|
-
expect(subject.
|
654
|
+
describe 'application_secret_strategy' do
|
655
|
+
it 'is plain by default' do
|
656
|
+
expect(subject.application_secret_strategy).to eq(Doorkeeper::SecretStoring::Plain)
|
657
|
+
expect(subject.application_secret_fallback_strategy).to eq(nil)
|
589
658
|
end
|
590
659
|
|
591
660
|
context 'when provided' do
|
592
|
-
|
661
|
+
before do
|
662
|
+
Doorkeeper.configure do
|
663
|
+
hash_application_secrets
|
664
|
+
end
|
665
|
+
end
|
593
666
|
|
594
|
-
it 'will enable
|
595
|
-
expect(subject.
|
667
|
+
it 'will enable hashing for applications' do
|
668
|
+
expect(subject.application_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)
|
669
|
+
expect(subject.application_secret_fallback_strategy).to eq(nil)
|
596
670
|
end
|
597
671
|
end
|
598
|
-
end
|
599
672
|
|
600
|
-
|
601
|
-
|
602
|
-
|
673
|
+
context 'when manually provided with invalid constant' do
|
674
|
+
it 'raises an exception' do
|
675
|
+
expect {
|
676
|
+
Doorkeeper.configure do
|
677
|
+
hash_application_secrets using: 'does not exist'
|
678
|
+
end
|
679
|
+
}.to raise_error(NameError)
|
680
|
+
end
|
681
|
+
end
|
603
682
|
|
604
|
-
|
605
|
-
|
606
|
-
|
683
|
+
context 'when provided with fallback' do
|
684
|
+
before do
|
685
|
+
Doorkeeper.configure do
|
686
|
+
hash_application_secrets fallback: :plain
|
687
|
+
end
|
607
688
|
end
|
608
689
|
|
609
|
-
|
690
|
+
it 'will enable hashing for applications' do
|
691
|
+
expect(subject.application_secret_strategy).to eq(Doorkeeper::SecretStoring::Sha256Hash)
|
692
|
+
expect(subject.application_secret_fallback_strategy).to eq(Doorkeeper::SecretStoring::Plain)
|
693
|
+
end
|
610
694
|
end
|
611
695
|
end
|
612
696
|
end
|
@@ -44,4 +44,16 @@ describe 'Expirable' do
|
|
44
44
|
expect(subject.expires_in_seconds).to be_nil
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
describe :expires_at do
|
49
|
+
it 'should return the expiration time of the token' do
|
50
|
+
allow(subject).to receive(:expires_in).and_return(2.minutes)
|
51
|
+
expect(subject.expires_at).to be_a(Time)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should return nil when expires_in is nil' do
|
55
|
+
allow(subject).to receive(:expires_in).and_return(nil)
|
56
|
+
expect(subject.expires_at).to be_nil
|
57
|
+
end
|
58
|
+
end
|
47
59
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe 'Reusable' do
|
6
|
+
subject do
|
7
|
+
Class.new do
|
8
|
+
include Doorkeeper::Models::Reusable
|
9
|
+
end.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe :reusable? do
|
13
|
+
it 'is reusable if its expires_in is nil' do
|
14
|
+
allow(subject).to receive(:expired?).and_return(false)
|
15
|
+
allow(subject).to receive(:expires_in).and_return(nil)
|
16
|
+
expect(subject).to be_reusable
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is reusable if its expiry has crossed reusable limit' do
|
20
|
+
allow(subject).to receive(:expired?).and_return(false)
|
21
|
+
allow(Doorkeeper.configuration).to receive(:token_reuse_limit).and_return(90)
|
22
|
+
allow(subject).to receive(:expires_in).and_return(100.seconds)
|
23
|
+
allow(subject).to receive(:expires_in_seconds).and_return(20.seconds)
|
24
|
+
expect(subject).to be_reusable
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is not reusable if its expiry has crossed reusable limit' do
|
28
|
+
allow(subject).to receive(:expired?).and_return(false)
|
29
|
+
allow(Doorkeeper.configuration).to receive(:token_reuse_limit).and_return(90)
|
30
|
+
allow(subject).to receive(:expires_in).and_return(100.seconds)
|
31
|
+
allow(subject).to receive(:expires_in_seconds).and_return(5.seconds)
|
32
|
+
expect(subject).not_to be_reusable
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'is not reusable if it is already expired' do
|
36
|
+
allow(subject).to receive(:expired?).and_return(true)
|
37
|
+
expect(subject).not_to be_reusable
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'Doorkeeper::Models::Scopes' do
|
4
4
|
subject do
|
5
|
-
Class.new(
|
5
|
+
Class.new(Struct.new(:scopes)) do
|
6
6
|
include Doorkeeper::Models::Scopes
|
7
7
|
end.new
|
8
8
|
end
|
@@ -21,6 +21,18 @@ describe 'Doorkeeper::Models::Scopes' do
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
describe :scopes= do
|
25
|
+
it 'accepts String' do
|
26
|
+
subject.scopes = 'private admin'
|
27
|
+
expect(subject.scopes_string).to eq('private admin')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'accepts Array' do
|
31
|
+
subject.scopes = %w[private admin]
|
32
|
+
expect(subject.scopes_string).to eq('private admin')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
24
36
|
describe :scopes_string do
|
25
37
|
it 'is a `Scopes` class' do
|
26
38
|
expect(subject.scopes_string).to eq('public admin')
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe 'SecretStorable' do
|
6
|
+
let(:clazz) do
|
7
|
+
Class.new do
|
8
|
+
include Doorkeeper::Models::SecretStorable
|
9
|
+
|
10
|
+
def self.find_by(*)
|
11
|
+
raise 'stub this'
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_column(*)
|
15
|
+
raise 'stub this'
|
16
|
+
end
|
17
|
+
|
18
|
+
def token
|
19
|
+
raise 'stub this'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
let(:strategy) { clazz.secret_strategy }
|
24
|
+
|
25
|
+
describe :find_by_plaintext_token do
|
26
|
+
subject { clazz.send(:find_by_plaintext_token, 'attr', 'input') }
|
27
|
+
|
28
|
+
it 'forwards to the secret_strategy' do
|
29
|
+
expect(strategy)
|
30
|
+
.to receive(:transform_secret)
|
31
|
+
.with('input')
|
32
|
+
.and_return 'found'
|
33
|
+
|
34
|
+
expect(clazz)
|
35
|
+
.to receive(:find_by)
|
36
|
+
.with('attr' => 'found')
|
37
|
+
.and_return 'result'
|
38
|
+
|
39
|
+
|
40
|
+
expect(subject).to eq 'result'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'calls find_by_fallback_token if not found' do
|
44
|
+
expect(clazz)
|
45
|
+
.to receive(:find_by)
|
46
|
+
.with('attr' => 'input')
|
47
|
+
.and_return nil
|
48
|
+
|
49
|
+
expect(clazz)
|
50
|
+
.to receive(:find_by_fallback_token)
|
51
|
+
.with('attr', 'input')
|
52
|
+
.and_return 'fallback'
|
53
|
+
|
54
|
+
expect(subject).to eq 'fallback'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe :find_by_fallback_token do
|
59
|
+
subject { clazz.send(:find_by_fallback_token, 'attr', 'input') }
|
60
|
+
let(:fallback) { double(::Doorkeeper::SecretStoring::Plain) }
|
61
|
+
|
62
|
+
it 'returns nil if none defined' do
|
63
|
+
expect(clazz.fallback_secret_strategy).to eq nil
|
64
|
+
expect(subject).to eq nil
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'if a fallback strategy is defined' do
|
68
|
+
let(:resource) { double('Token model') }
|
69
|
+
before do
|
70
|
+
allow(clazz).to receive(:fallback_secret_strategy).and_return(fallback)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'calls the strategy for lookup' do
|
74
|
+
expect(clazz)
|
75
|
+
.to receive(:find_by)
|
76
|
+
.with('attr' => 'fallback')
|
77
|
+
.and_return(resource)
|
78
|
+
|
79
|
+
expect(fallback)
|
80
|
+
.to receive(:transform_secret)
|
81
|
+
.with('input')
|
82
|
+
.and_return('fallback')
|
83
|
+
|
84
|
+
# store_secret will call the resource
|
85
|
+
expect(resource)
|
86
|
+
.to receive(:attr=)
|
87
|
+
.with('new value')
|
88
|
+
|
89
|
+
# It will upgrade the secret automtically using the current strategy
|
90
|
+
expect(strategy)
|
91
|
+
.to receive(:transform_secret)
|
92
|
+
.with('input')
|
93
|
+
.and_return('new value')
|
94
|
+
|
95
|
+
expect(resource).to receive(:update).with('attr' => 'new value')
|
96
|
+
expect(subject).to eq resource
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
describe :secret_strategy do
|
103
|
+
it 'defaults to plain strategy' do
|
104
|
+
expect(strategy).to eq Doorkeeper::SecretStoring::Plain
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe :fallback_secret_strategy do
|
109
|
+
it 'defaults to nil' do
|
110
|
+
expect(clazz.fallback_secret_strategy).to eq nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -73,7 +73,7 @@ module Doorkeeper::OAuth
|
|
73
73
|
expect(subject.error).to eq(:invalid_grant)
|
74
74
|
end
|
75
75
|
|
76
|
-
it 'skips token creation if there is a matching one' do
|
76
|
+
it 'skips token creation if there is a matching one reusable' do
|
77
77
|
scopes = grant.scopes
|
78
78
|
|
79
79
|
Doorkeeper.configure do
|
@@ -88,6 +88,23 @@ module Doorkeeper::OAuth
|
|
88
88
|
expect { subject.authorize }.to_not(change { Doorkeeper::AccessToken.count })
|
89
89
|
end
|
90
90
|
|
91
|
+
it 'creates token if there is a matching one but non reusable' do
|
92
|
+
scopes = grant.scopes
|
93
|
+
|
94
|
+
Doorkeeper.configure do
|
95
|
+
orm DOORKEEPER_ORM
|
96
|
+
reuse_access_token
|
97
|
+
default_scopes(*scopes)
|
98
|
+
end
|
99
|
+
|
100
|
+
FactoryBot.create(:access_token, application_id: client.id,
|
101
|
+
resource_owner_id: grant.resource_owner_id, scopes: grant.scopes.to_s)
|
102
|
+
|
103
|
+
allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)
|
104
|
+
|
105
|
+
expect { subject.authorize }.to change { Doorkeeper::AccessToken.count }.by(1)
|
106
|
+
end
|
107
|
+
|
91
108
|
it "calls configured request callback methods" do
|
92
109
|
expect(Doorkeeper.configuration.before_successful_strategy_response)
|
93
110
|
.to receive(:call).with(subject).once
|